得到源代码
获得源代码后,使用搜索工具,例如PowerGREP搜索源码
搜索关键词如下
select
update
delete
insert
sql
from
database
搜索后,可以看到结果中出现了很多条sql语句。
这时候,我们想要查看代码,判断这些操纵sql的代码片段是否存在sql注入。
我们需要知道那种代码看起来就有sql注入漏洞,哪种代码看起来就是安全的没有漏洞。
存在sql注入的错误写法如下,如果你看到代码中传入参数的格式如下,那么只要此处的代码被调用,就一定存在sql注入。
cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT spam, eggs, sausage FROM breakfast WHERE price < %s" % (max_price,))
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");
安全的sql操作代码
那么什么情况下,能识别到当前的代码没有sql注入漏洞呢。
下面的代码均为安全的代码,可以防止sql注入,如果你搜索出的代码如下,那就证明代码没有sql注入漏洞。
下面的内容也可以被开发借鉴,写出安全的sql操作代码
# 防止sql注入的安全写法
cursor.execute("SELECT admin FROM users WHERE username = %s", (username, ));
cursor.execute("SELECT spam, eggs, sausage FROM breakfast WHERE price < %s", (max_price,))
cursor.execute("SELECT spam FROM eggs WHERE lumberjack = :1", (lumberjack,))
cursor.execute("SELECT spam FROM eggs WHERE lumberjack = :jack", {'jack': lumberjack})
cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': jack});
cursor.execute("SELECT spam FROM eggs WHERE lumberjack = ?", (lumberjack,))
参数化查询
mysql.connector和pymysql使用%s作为占位符,防范 SQL 注入的最佳方法是使用参数化查询,可以防止sql注入
import pymysql
conn = pymysql.connect(host="localhost",
port=3306,
user="root",
password="mysql123456",
database="python",
charset="utf8")
# 定义查询和参数,下面可以防止sql注入
query = "SELECT * FROM t_oauth_users WHERE username=%s AND email=%s"
params = ('user1', '[email protected]')
# 执行查询
cursor.execute(query, params)
import mysql.connector
conn = mysql.connector.connect(user='your_username', password='your_password', host='localhost', database='your_database')
cursor = conn.cursor()
query = "SELECT * FROM t_oauth_users WHERE username=%s AND email=%s"
params = ('user1', '[email protected]')
cursor.execute(query, params)
使用 %(name)s 命名占位符可以防止sql注入
query = "SELECT * FROM t_oauth_users WHERE username=%(username)s AND email=%(email)s"
params = {'username': 'user1', 'email': '[email protected]'}
Python 库提供了 API 来对大多数可用的数据库技术执行参数化查询,每个代码片段中的代码都展示了使用一些最常见的 Python 数据库库的参数化查询来调用方法的推荐方法。
PyMySQL,MySQL-python
MySQL connector, PyGreSQL, Psycopg, pymssql 可以防止sql注入
cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
SQLAlchemy 可以防止sql注入
stmt = sqlalchemy.sql.text("SELECT * FROM users WHERE username = :username and password = :password")
conn.execute(stmt, {"username": username, "password": password })
sqlite3, pyodbc 可以防止sql注入
cursor.execute("SELECT * FROM users WHERE username = ? AND password = ?", (username, password))
c.execute('select * from stocks where name=?', t)
?会把参数当做值来处理,它会转义特殊字符,并且在输入前后加上’’
以select * from user where id = ?语句为例,传入1 or 1=1
select * from user where id = '1 or 1 = 1'
转义用户输入防止sql注入
在某些情况下,需要将用户提供的数据直接包含在 SQL 查询中。
例如,在构建动态搜索功能时,用户可能会输入搜索词,该搜索词需要直接包含在 SQL 查询中才能检索相关结果。
在这种情况下,正确转义用户输入以防止 SQL 注入攻击非常重要。
在 Python 中,我们可以使用psycopg2.sql.SQL类或mysql.connector.connector.escape_string方法来转义用户输入。
转义用户输入会将用户提供的数据中的任何特殊字符替换为转义字符,从而可以在 SQL 查询中安全使用。
在此示例中,该类sql.SQL用于创建带有名称占位符的 SQL 查询。
sql.Identifier然后使用该方法对名称的值进行转义,以防止 SQL 注入。
import psycopg2
from psycopg2 import sql
def dynamic_search(search_term):
with psycopg2.connect(database="mydatabase", user="mydatabaseuser", password="mypassword", host="localhost", port="5432") as conn:
cursor = conn.cursor()
# Use psycopg2.sql to build and execute the query
query = sql.SQL("SELECT * FROM mytable WHERE name LIKE {};").format(sql.Identifier(search_term))
cursor.execute(query)
results = cursor.fetchall()
return results
以下是我们如何使用 MySQL 库转义用户输入。
通过使用该mysql.connector.connector.escape_string函数正确地转义用户输入,我们能够安全地将用户提供的数据包含在查询中。
import mysql.connector
def dynamic_search ( search_term ):
with mysql.connector.connect(user= 'mydatabaseuser' , password= 'mypassword' , host= 'localhost' , database= 'mydatabase' ) as conn:
cursor = conn.cursor()
# 使用 escape_string 函数正确转义用户输入
escaped_search_term = mysql.connector.connector.escape_string(search_term)
query = "SELECT * FROM mytable WHERE name LIKE '{}'".format (escaped_search_term)
cursor.execute(query)
results = cursor.fetchall()
return results
Django ORM—ORM 框架防止注入
如果你完全避免使用原生SQL查询,并且只使用Django ORM提供的高级查询方法(如 filter, get, exclude 等),那么你以避免SQL注入的风险。Django ORM在设计时已经考虑到了安全性,会自动对用户输入进行转义和参数化处理,从而防止SQL注入。
Django ORM 提供了多种安全机制来防止SQL注入,主要包括:
-
参数化查询
Django ORM 内部使用参数化查询来构建SQL语句。这意味着用户输入的数据会被作为参数传递给数据库驱动程序,而不是直接拼接到SQL字符串中。
2. 自动转义
Django ORM 会自动对用户输入的数据进行转义,确保数据不会被解释为SQL代码。
1. 简单查询
from django.http import JsonResponse
from book.models import Book
def search_books(request):
keyword = request.GET.get('keyword', '')
# 使用Django ORM进行查询
books = Book.objects.filter(title__icontains=keyword)
results = list(books.values())
return JsonResponse({'results': results})
2. 复合查询
from django.http import JsonResponse
from book.models import Book
def advanced_search(request):
keyword = request.GET.get('keyword', '')
author = request.GET.get('author', '')
# 使用Django ORM进行复合查询
books = Book.objects.filter(title__icontains=keyword, author__icontains=author)
results = list(books.values())
return JsonResponse({'results': results})
3. 排序和分页
from django.http import JsonResponse
from book.models import Book
def list_books(request):
page = int(request.GET.get('page', 1))
page_size = int(request.GET.get('page_size', 10))
books = Book.objects.order_by('publication_date')[(page - 1) * page_size:page * page_size]
results = list(books.values())
return JsonResponse({'results': results})
防止sql注入的其它方法
不要在数据报文中显示具体的sql语句,防止被攻击者测试攻击。
把要执行的sql语句写死在代码里,而不是直接执行用户输入的数据
错误方法:系统和数据库管理员在安装数据库服务器时允许以roots SYSTEM或Administrator特权系统用户账户身份执行操作。
正确方法:应该始终以普通用户身份运行服务器上的服务,降低用户权限,将用户权限只限于本服务。
原文始发于微信公众号(SecurityBug):源码审计是否存在sql注入,安全和危险的sql操作代码
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论