面向开发人员梳理的代码安全指南,旨在梳理 API
层面的风险点并提供详实可行的安全编码方案。基于 DevSecOps
理念,我们希望用开发者更易懂的方式阐述安全编码方案,引导从源头规避漏洞。
Python代码安全指南
代码书写完毕之后的,后续工作,如加密代码之类的!
DES
和 3DES
已经不再适用于现代应用程序,应改为使用 AES
。6
个月以上bash
# 口令强度须同时满足
1.密码长度大于14位
2.必须包含下列元素:大小写英文字母、数字、特殊字符
3.不得使用各系统、程序的默认初始密码
4.不能与最近6次使用过的密码重复
5.不得与其他外部系统使用相同的密码
DES
和 3DES
)加密存储口令salt
对口令进行加密存储发布系统或者上线环境前,需要注意的问题!
Python 3.6+
版本bash
# 为什么要这么做?
由于Python2在2020年停止维护,相关组件的漏洞不能得到及时修复与维护!
AK/SK
、IP
、数据库账密等配置信息KMS
密钥管理系统这是一条华丽的分割线
编写代码是需要考虑和思考的问题!
python
# Cerberus示例
v = Validator({'name': {'type': 'string'}})
v.validate({'name': 'john doe'})
# jsonschema示例
schema = {
"type" : "object",
"properties" : {
"price" : {"type" : "number"},
"name" : {"type" : "string"},
},
}
validate(instance={"name" : "Eggs", "price" : 34.99}, schema=schema)
SQL
语句,强制区分数据和命令,避免产生 SQL
注入漏洞。python
# 错误示例
import mysql.connector
mydb = mysql.connector.connect(
... ...
)
cur = mydb.cursor()
userid = get_id_from_user()
# 使用%直接格式化字符串拼接SQL语句
cur.execute("SELECT `id`, `password` FROM `auth_user` WHERE `id`=%s " % (userid,))
myresult = cur.fetchall()
python
# 安全示例
import mysql.connector
mydb = mysql.connector.connect(
... ...
)
cur = mydb.cursor()
userid = get_id_from_user()
# 将元组以参数的形式传入
cur.execute("SELECT `id`, `password` FROM `auth_user` WHERE `id`=%s " , (userid,))
myresult = cur.fetchall()
ORM
框架来操作数据库,如:使用 SQLAlchemy
。python
# 安装sqlalchemy并初始化数据库连接
# pip install sqlalchemy
from sqlalchemy import create_engine
# 初始化数据库连接,修改为你的数据库用户名和密码
engine = create_engine('mysql+mysqlconnector://user:password@host:port/DATABASE')
python
# 引用数据类型
from sqlalchemy import Column, String, Integer, Float
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
# 定义 Player 对象:
class Player(Base):
# 表的名字:
__tablename__ = 'player'
# 表的结构:
player_id = Column(Integer, primary_key=True, autoincrement=True)
team_id = Column(Integer)
player_name = Column(String(255))
height = Column(Float(3, 2))
python
# 增删改查
from sqlalchemy.orm import sessionmaker
# 创建 DBSession 类型:
DBSession = sessionmaker(bind=engine)
# 创建 session 对象:
session = DBSession()
# 增:
new_player = Player(team_id=101, player_name="Tom", height=1.98)
session.add(new_player)
# 删:
row = session.query(Player).filter(Player.player_name=="Tom").first()
session.delete(row)
# 改:
row = session.query(Player).filter(Player.player_name=="Tom").first()
row.height = 1.99
# 查:
rows = session.query(Player).filter(Player.height >= 1.88).all()
# 提交即保存到数据库:
session.commit()
# 关闭 session:
session.close()
SQL
语句时,必须对参数进行安全过滤。python
def sql_filter(sql, max_length=20):
dirty_stuff = ["\"", "\\", "/", "*", "'", "=", "-", "#", ";", "<", ">", "+",
"&", "$", "(", ")", "%", "@", ","]
for stuff in dirty_stuff:
sql = sql.replace(stuff, "x")
return sql[:max_length]
os.system()
、os.popen()
、subprocess.call()
等。API
进行文件操作而非直接调用操作系统命令。python
import os
import sys
import shlex
domain = sys.argv[1]
# 替换可以用来注入命令的字符为空
badchars = "\n&;|'\"$()`-"
for char in badchars:
domain = domain.replace(char, " ")
result = os.system("nslookup " + shlex.quote(domain))
XXE
攻击。python
from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))
WebShell
等文件。python
import os
ALLOWED_EXTENSIONS = ['txt','jpg','png']
def allowed_file(filename):
if ('.' in filename and
'..' not in filename and
os.path.splitext(filename)[1].lower() in ALLOWED_EXTENSIONS):
return filename
return None
WEB
容器的可执行目录(appBase
)。python
import os
upload_dir = '/tmp/upload/' # 预期的上传目录
file_name = '../../etc/hosts' # 用户传入的文件名
absolute_path = os.path.join(upload_dir, file_name) # /tmp/upload/../../etc/hosts
normalized_path = os.path.normpath(absolute_path) # /etc/hosts
if not normalized_path.startswith(upload_dir): # 检查最终路径是否在预期的上传目录中
raise IOError()
python
import uuid
def random_filename(filename):
ext = os.path.splitext(filename)[1]
new_filename = uuid.uuid4().hex + ext
return new_filename
bash
# 当程序需要从用户提供的URL地址获取信息时
# 如指定的URL地址获取网页文本内容、加载指定地址的图片、进行下载等操作时,需对URL地址进行安全校验
1.只允许HTTP或HTTPS协议
2.解析目标URL,获取其host
3.解析host,获取host指向的IP地址转换成long型
4.检查IP地址是否为内网IP
# 以RFC定义的专有网络为例
# 如有自定义私有网段亦应加入禁止访问列表
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8
5.请求URL
6.如果有跳转,跳转后执行1,否则对URL发起请求
bash
# X-Content-Type-Options
添加“X-Content-Type-Options”响应头并将其值设置为“nosniff”。
# HttpOnly
控制用户登鉴权的Cookie字段应当设置HttpOnly属性以防止被XSS漏洞/JavaScript 操纵泄漏。
# X-Frame-Options
置X-Frame-Options响应头,并根据需求合理设置其允许范围。
该头用于指示浏览器禁止当前页面在 frame、 iframe、embed 等标签中展现,从而避免点击劫持问题。
它有三个可选的值:
DENY: 浏览器会拒绝当前页面加载任何frame页面;
SAMEORIGIN: 则frame页面的地址只能为同源域名下的页面
ALLOW-FROM origin: 可以定 义允许frame加载的页面地址。
python
# 推荐使用mozilla维护的bleach库来进行过滤
import bleach
bleach.clean('an <script>evil()</script> example')
# u'an <script>evil()</script> example'
SHA2
、RSA
等算法进行加密存储cvv
码及日志禁止存储3***************1
6
位字符,如 134******48
4
位字符,如 ************8639
python
# 不要采取这种方式
admin_login_url = "xxxx/login"
# 安全示例
admin_login_url = "xxxx/ranD0Str"
CMS
)和数据权限校验。bash
1. 验证当前用户的登录态
2. 从可信结构中获取经过校验的当前请求账号的身份信息(如session),禁止从用户请求参数或Cookie中获取外部传入不可信用户身份直接进行查询
3. 校验当前用户是否具备该操作权限
4. 校验当前用户是否具备所操作数据的权限
5. 校验当前操作是否账户是否预期账户
try/except/finally
处理系统异常,避免出错信息输出到前端。debug
模式,或将程序运行日志输出到前端。使用 Flask 框架编写代码是需要考虑和思考的问题!
Flask
文档中的安全注意事项 https://flask.palletsprojects.com/en/latest/security/使用 Django 框架编写代码是需要考虑和思考的问题!
Django
自带的安全特性开启 https://docs.djangoproject.com/en/3.0/topics/security/Django
自带的安全特性对 XSS
、CSRF
、SQL
注入、点击劫持等类型漏洞可以起到较好防护效果。应尽量避免关闭这些安全特性。本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。