編碼與加密
1、編碼
(1)ASCII編碼
ASCII碼默認使用7位二進制數來表示所有大寫字母、小寫字母、數字(0~9)、標點符號和特殊的控制符
(2)Base64編碼
Base64基於64個可打印字符來表示8位二進制數據,用於解決不可打印的字符(如非英文的字符)在網絡傳輸過程中造成的亂碼現象
2、基於編碼的反爬蟲設計
(1)Base64編碼反爬
Base64編碼時所用的對照表是固定的,也就是說它的編碼過程是可逆的。這意味着我們只需要將編碼的流程倒置,就能得到解碼的方法
一般帶有“==”符號或者“=”符號的字符串時,可能就是Base64編碼字符串後得到的結果,按照Base64解碼規則進行倒推,得到原字符
from base64 import b64decode
code = [ "d3d3Lmh1YXdlaS5jb20=" , "d3d3Lmp1ZWppbi5pbQ==" ]
for c in code:
s = b64decode( c) . decode( "utf8" )
print ( s)
難點:Base64編碼和解碼時都是將原本8位的二進制數轉換成6位的二進制數。如果我們改動位數,將其設置爲5位或者4位,那麼就可以實現新的編碼規則,這時如果還是使用原先的方法進行解碼,就可能解碼不成功。改動位數,將其設置爲5位或者4位
(2)MD5反爬
MD5是一種廣泛使用的散列函數,它能夠將任意長度的消息轉換成128位的消息摘要。與Base64編碼不同的是,MD5是不可逆的,這意味着我們可以將字符串轉換爲MD5值,但無法將MD5值換成MD5值轉換成原字符串。
相比Base64編碼,MD5運算過程要複雜很多。由於MD5在運算過程中使用了補位、追加和移位等操作,所以他人無法從輸出結果倒推出輸入字符,這個特性被稱爲“不可逆”。
MD5可以對任意長度的消息進行運算,輸出固定位數(128位)的結果,這個特性被稱爲“壓縮”
(3)對稱加密與AES
加密和解密時使用同一個密鑰的加密方式叫做對稱加密,使用不同密鑰的是非對稱加密
對稱加密
加密_密鑰A -- --
解密_密鑰A -- --
明文
密文
加密_密鑰A _公鑰 -- --
解密_密鑰B_私鑰 -- --
明文
密文
相對於非對稱加密來說,對稱加密的速度更快,速度的優勢使得它更適合大量數據加密的場景,常見的堆成加密算法有DES、3DES、BLOWFISH、RC5和AES等
AES加密過程中的操作是可逆的,關鍵在於密鑰
from Crypto. Cipher import AES
aes1 = AES. new( '63f09k56nv2b10cf' , AES. MODE_CBC, "01pv928nv2i5ss68" )
message = "Hi I am from the earth number 77"
print ( f"待加密消息{message}" )
cipher_text = aes1. encrypt( message)
aes2 = AES. new( '63f09k56nv2b10cf' , AES. MODE_CBC, "01pv928nv2i5ss68" )
plaint_text = aes2. decrypt( cipher_text)
print ( f"密文{cipher_text}" )
print ( f"明文{plaint_text.decode('utf8')}" )
(4)非對稱加密與RSA
在不傳遞密鑰的情況下實現加密和解密操作,與對稱加密不同的是,這種方式需要用到兩個密鑰:公鑰和私鑰。
公鑰和私鑰是一對,如果用該公鑰對數據進行加密,那麼只有用對應的私鑰才能夠解密數據,反之亦然;用的最多的是RSA,可逆
from Crypto import Random
from Crypto. PublicKey import RSA
from Crypto. Cipher import PKCS1_v1_5
import base64
message = 'async'
rsa = RSA. generate( 1024 , Random. new( ) . read)
private_key = rsa. exportKey( )
public_key = rsa. publickey( ) . exportKey( )
print ( private_key. decode( 'utf8' ) )
print ( public_key. decode( 'utf8' ) )
with open ( 'private.pem' , 'wb' ) as f:
f. write( private_key)
with open ( 'public.pem' , 'wb' ) as f:
f. write( public_key)
with open ( 'public.pem' , 'r' ) as f:
pub = f. read( )
pubkey = RSA. importKey( pub)
cipher = PKCS1_v1_5. new( pubkey)
c = base64. b64encode( cipher. encrypt( message. encode( 'utf8' ) ) ) . decode( 'utf8' )
with open ( 'private.pem' , 'r' ) as f:
pri = f. read( )
prikey = RSA. importKey( pri)
cipher = PKCS1_v1_5. new( prikey)
m = cipher. decrypt( base64. b64decode( c) , 'error' ) . decode( 'utf8' )
print ( f'消息原文:{message}\n消息密文:{c}\n解密結果:{m}' )
3、JavaScript代碼混淆
網頁打開速度:即資源加載速度和頁面渲染速度;
代碼壓縮:資源加載速度的常用優化方法是壓縮,壓縮的對象包括HTML文檔、圖片和JavaScript文件等。壓縮後的文件體積相對較小,不僅有利於網絡傳輸,還能起到保護代碼的作用。這是因爲在壓縮過程中會刪除註釋、空格和換行符等元素,將多個JavaScript文件合併以及縮短變量名。
隨着瀏覽器和各大IDE推出代碼格式化功能,文件壓縮對代碼的保護就被削弱了;爲了更好地保護代碼,開發者想到了代碼混淆這樣的辦法
常見的混淆方法:正則替換、代碼編碼和代碼複雜化等
(1)正則替換之變量名替換
用簡短的字母替換方法中的變量名,也可以在此基礎上增加空格和換行符的刪除操作,原代碼如下
function transToDict ( name, age, vip) {
result = "{name:" + name + ", age:" + age + ", isVIP:" + vip + ",}" ;
console. log ( result) ;
return result;
}
(2)正則替換之進制替換
除了能解析Base64編碼外,瀏覽器還能解析十六進制的字符。原代碼如下
var ins = 1 + 2 , ss = "abc" ;
function pack ( a, b) {
return a + b + ss;
} ;
var pp = pack ( ins, 6 ) ;
console. log ( pp) ;
(3)代碼編碼之Base64
瀏覽器會自動解析Base64編碼後的內容,切換到Console可以看到輸出結果
(4)代碼編碼之AAEncode
代碼混淆不僅是替換一些字符,一些奇怪的編碼也能起到保護代碼的作用。常見的編碼方法有AAEncode和JJEncode。
AAEncode能將JavaScript代碼轉換爲顏文字,而JJEncode則將代碼轉換爲“$”符號、"_“符號和”+"符號。
(5)代碼編碼之JJEncode
JJEncode與AAEncode的編碼原理相似,但輸出的符號不同。
(6)代碼複雜化之訪問符
有些混淆方法則是改變對象的訪問符,如將原本用",“訪問對象的方式改爲用”[]"訪問,在此基礎上,加入十六進制和Unicode混淆手段
(7)代碼複雜化之Packer
Packer是代碼混淆中常用的方法,它能將原代碼變得複雜,降低可讀性
4、混淆代碼的還原
混淆並不是加密,混淆後的代碼肯定能夠在瀏覽器中執行,否則混淆就失去了意義。所以說,混淆的代碼可以被還原,只不過不同的混淆手段在還原時耗費的時間不同,
代碼還原有一定的規律,例如正則替換這種混淆方法通常會將數字和字母轉換爲十六進制
在調用Base64編碼後得到的代碼時,需要使用eval關鍵字
eval ( atob( "basefahuh==" ) )
AAEncode 和JJEncode的編碼結果可以直接運行,不需要再代碼前加eval。這兩種編碼的還原方式很簡單,只需要用到console shell工具,需要刪除最後一組括號內的信息
Packer的還原和Base64一樣,需要使用eval關鍵字,只需要用到console shell工具即可看到原代碼
5、混淆原理
6、前端禁止事件
(1)禁止鼠標事件
屬性
描述
onclick
當用戶點擊某個對象時調用的事件句柄
oncontextmenu
在用戶點擊鼠標右鍵打開上下文菜單時觸發
ondbclick
當用戶雙擊某個對象時調用的事件與句柄
onmousedown
鼠標按鈕按下
onmouseenter
當鼠標指針移動到元素時觸發
onmousemove
鼠標被移動
onmouseover
鼠標移到某元素之上
onmouseout
鼠標從某元素移開
onmouseup
鼠標按鍵被鬆開
開發者可以通過JavaScript在HTML中註冊oncontextmenu事件檢測鼠標右鍵,當檢測到鼠標右鍵被按下時重置事件或返回false,這樣就能夠實現鼠標右鍵的檢測和禁用,進而限制開發者工具的喚起操作;但是ctr+shift+I可以
<!DOCTYPE html>
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Title</ title>
</ head>
< body>
< p> hello,today</ p>
< script type = " text/javascript" >
document. oncontextmenu = function ( ) {
event. returnValue = false
}
</ script>
</ body>
</ html>
<!DOCTYPE html>
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Title</ title>
</ head>
< body>
< p> hello,today</ p>
< script type = " text/javascript" >
document. oncopy = function ( ) {
event. returnValue = false
}
</ script>
</ body>
</ html>
(2)禁止鍵盤事件
事件
值
描述
VK_F5
0x74(116)
F5鍵
VK_F12
0x7B(123)
F12鍵
VK_DELETE
0x2E(46)
Delete鍵
VK_PAGE_UP
0x21(33)
Page up鍵
VK_PAGE_DOWN
0x22(34)
Page down鍵
<!DOCTYPE html>
< html lang = " en" >
< head>
< meta charset = " UTF-8" >
< title> Title</ title>
</ head>
< body>
< p> 用戶無法通過F12鍵喚起開發者工具</ p>
< script type = " text/javascript" >
document. onkeydown = function ( ) {
if ( window. event && window. event. keyCode == 123 ) {
event. returnValue= false
}
event. returnValue = false
}
</ script>
</ body>
</ html>
(3)其他debug時,使用while 1啓動無限debug,達到干擾爬蟲工程師調試代碼的目的