程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

Python代碼安全指南

編輯:Python

面向開發人員梳理的代碼安全指南,旨在梳理 API 層面的風險點並提供詳實可行的安全編碼方案。基於 DevSecOps 理念,我們希望用開發者更易懂的方式闡述安全編碼方案,引導從源頭規避漏洞。

Python代碼安全指南

通用類

1. 代碼實現

代碼書寫完畢之後的,後續工作,如加密代碼之類的!

1.1 加密算法

  • 【必須】避免使用不安全的對稱加密算法
    • DES3DES 已經不再適用於現代應用程序,應改為使用 AES

1.2 程序日志

  • 【建議】對每個重要行為都記錄日志
    • 確保重要行為都記錄日志,且可靠保存 6 個月以上
  • 【建議】禁止將未經驗證的用戶輸入直接記錄日志
    • 當日志條目包含未經淨化的用戶輸入時會引發記錄注入漏洞
    • 惡意用戶會插入偽造的日志數據,從而讓系統管理員以為是系統行為
  • 【建議】避免在日志中保存敏感信息
    • 不能在日志保存密碼(包括明文密碼和密文密碼)、密鑰和其它敏感信息

1.3 系統口令

  • 【必須】禁止使用空口令、弱口令、已洩露口令
  • 【必須】口令強度要求

bash

# 口令強度須同時滿足
1.密碼長度大於14位
2.必須包含下列元素:大小寫英文字母、數字、特殊字符
3.不得使用各系統、程序的默認初始密碼
4.不能與最近6次使用過的密碼重復
5.不得與其他外部系統使用相同的密碼
  • 【必須】口令存儲安全
    • 禁止明文存儲口令
    • 禁止使用弱密碼學算法(如 DES3DES)加密存儲口令
    • 使用不可逆算法和隨機 salt 對口令進行加密存儲
  • 【必須】禁止傳遞明文口令
  • 【必須】禁止在不安全的信道中傳輸口令

2. 配置&環境

發布系統或者上線環境前,需要注意的問題!

2.1 版本選擇

  • 【建議】使用 Python 3.6+的版本
    • 新增的項目應使用 Python 3.6+ 版本

bash

# 為什麼要這麼做?
由於Python2在2020年停止維護,相關組件的漏洞不能得到及時修復與維護!

2.2 第三方包安全

  • 【必須】禁止使用不安全的組件

2.3 配置信息

  • 【必須】密鑰存儲安全
    • 在使用對稱密碼算法時,需要保護好加密密鑰。當算法涉及敏感、業務數據時,可通過非對稱算法協商加密密鑰
    • 其他較為不敏感的數據加密,可以通過變換算法等方式保護密鑰
  • 【必須】禁止硬編碼敏感配置
    • 禁止在源碼中硬編碼 AK/SKIP、數據庫賬密等配置信息
    • 應使用配置系統或 KMS 密鑰管理系統

後台類

這是一條華麗的分割線

1. 代碼實現

編寫代碼是需要考慮和思考的問題!

1.1 輸入驗證

  • 【必須】按類型進行數據校驗
    • 所有程序外部輸入的參數值,應進行數據校驗,校驗不通過應拒絕
    • 校驗內容包括但不限於:數據長度、數據范圍、數據類型與格式
    • 推薦使用組件:CerberusjsonschemaDjango-Validators

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)

1.2 SQL 操作

  • 【必須】使用參數化查詢
    • 使用參數化 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:[email protected]: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]

1.3 執行命令

  • 【建議】避免直接調用函數執行系統命令
    • 相關功能的實現應避免直接調用系統命令,如 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))

1.4 XML 讀寫

  • 【必須】禁用外部實體的方法
    • 禁用外部實體的方法,來預防 XXE 攻擊。

python

from lxml import etree
xmlData = etree.parse(xmlSource,etree.XMLParser(resolve_entities=False))

1.5 文件操作

  • 【必須】文件類型限制
    • 通過白名單對上傳或者下載的文件類型、大小進行嚴格校驗。
    • 僅允許業務所需文件類型上傳,避免上傳木馬、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)。
    • 建議使用 tempfile 庫處理臨時文件和臨時目錄。
  • 【必須】避免路徑穿越
    • 保存在本地文件系統時,必須對路徑進行合法校驗,避免目錄穿越漏洞。

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()
  • 【建議】避免路徑拼接
    • 文件目錄避免外部參數拼接。保存文件目錄建議後台寫死並對文件名進行校驗(字符類型、長度)。
  • 【建議】文件名 hash 化處理
    • 建議文件保存時,將文件名替換為隨機字符串。

python

import uuid
def random_filename(filename):
ext = os.path.splitext(filename)[1]
new_filename = uuid.uuid4().hex + ext
return new_filename

1.6 網絡請求

  • 【必須】限定訪問網絡資源地址范圍

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發起請求

1.7 響應輸出

  • 【必須】設置正確的 HTTP 響應包類型
    • 響應包的 HTTP 頭“Content-Type”必須正確配置響應包的類型,禁止非 HTML 類型的響應包設置為“text/html”。
  • 【必須】設置安全的 HTTP 響應頭

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加載的頁面地址。
  • 【必須】對外輸出頁面包含第三方數據時須進行編碼處理
    • 當響應“Content-Type”為“text/html”類型時,需要對響應體進行編碼處理

python

# 推薦使用mozilla維護的bleach庫來進行過濾
import bleach
bleach.clean('an <script>evil()</script> example')
# u'an &lt;script&gt;evil()&lt;/script&gt; example'

1.8 數據輸出

  • 【必須】敏感數據加密存儲
    • 敏感數據應使用 SHA2RSA 等算法進行加密存儲
    • 敏感數據應使用獨立的存儲層,並在訪問層開啟訪問控制
    • 包含敏感信息的臨時文件或緩存一旦不再需要應立刻刪除
  • 【必須】敏感信息必須由後台進行脫敏處理
    • 敏感信息須再後台進行脫敏後返回,禁止接口返回敏感信息交由前端/客戶端進行脫敏處理。
  • 【必須】高敏感信息禁止存儲、展示
    • 口令、密保答案、生理標識等鑒權信息禁止展示
    • 非金融類業務,信用卡 cvv 碼及日志禁止存儲
  • 【必須】個人敏感信息脫敏展示 在滿足業務需求的情況下,個人敏感信息需脫敏展示,如:
    • 身份證只顯示第一位和最後一位字符,如 3***************1
    • 移動電話號碼隱藏中間 6 位字符,如 134******48
    • 工作地址/家庭地址最多顯示到“區”一級
    • 銀行卡號僅顯示最後 4 位字符,如 ************8639
  • 【必須】隱藏後台地址
    • 若程序對外提供了登錄後台地址,應使用隨機字符串隱藏地址

python

# 不要采取這種方式
admin_login_url = "xxxx/login"
# 安全示例
admin_login_url = "xxxx/ranD0Str"

1.9 權限管理

  • 【必須】默認鑒權
    • 除非資源完全可對外開放,否則系統默認進行身份認證(使用白名單的方式放開不需要認證的接口或頁面)。
  • 【必須】授權遵循最小權限原則
    • 程序默認用戶應不具備任何操作權限。
  • 【必須】避免越權訪問
    • 對於非公共操作,應當校驗當前訪問賬號進行操作權限(常見於 CMS)和數據權限校驗。

bash

1. 驗證當前用戶的登錄態
2. 從可信結構中獲取經過校驗的當前請求賬號的身份信息(如session),禁止從用戶請求參數或Cookie中獲取外部傳入不可信用戶身份直接進行查詢
3. 校驗當前用戶是否具備該操作權限
4. 校驗當前用戶是否具備所操作數據的權限
5. 校驗當前操作是否賬戶是否預期賬戶
  • 【建議】及時清理不需要的權限
    • 程序應定期清理非必需用戶的權限。

1.10 異常處理

  • 【必須】不向對外錯誤提示
    • 應合理使用 try/except/finally 處理系統異常,避免出錯信息輸出到前端。
    • 對外環境禁止開啟 debug 模式,或將程序運行日志輸出到前端。
  • 【必須】禁止異常拋出敏感信息

2. Flask 安全

使用 Flask 框架編寫代碼是需要考慮和思考的問題!

  • 【必須】生產環境關閉調試模式
  • 【建議】遵循 Flask 安全規范
    • 參考 Flask 文檔中的安全注意事項 https://flask.palletsprojects.com/en/latest/security/

3. Django 安全

使用 Django 框架編寫代碼是需要考慮和思考的問題!

  • 【必須】生產環境關閉調試模式
  • 【建議】保持 Django 自帶的安全特性開啟
    • 保持 Django 自帶的安全特性開啟 https://docs.djangoproject.com/en/3.0/topics/security/
    • 在默認配置下,Django 自帶的安全特性對 XSSCSRFSQL 注入、點擊劫持等類型漏洞可以起到較好防護效果。應盡量避免關閉這些安全特性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved