Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
日常我們在工作時做安全測試或者日常的漏洞挖掘中,往往會遇到請求加密,參數加密的情況,而且絕大部分都是前端加密的情況,那么我們就可以手動調試跟蹤去解決這個加密問題,對我們自己要發送的數據進行編碼來幫助我們進行測試
一般定位都會采用關鍵函數跟蹤的方式,流程如下:
選擇需要定位的標簽,一般都是提交、登錄、保存等按鈕,通過這些按鈕再去查找其中對應的事件函數,然后到調試器欄ctrl+shift+f全局搜索函數名定位,定位到關鍵函數后,對其打個斷點,然后跟一跟基本就能確定走了什么加密流程。
隨后就需要手工在控制臺對函數進行調用對自己要測試的數據進行編碼,當然也可以把這些js文件保存下來,通過python腳本調用js函數。
burp也有一個jsEncrypter插件,詳情可以去github主頁瞅瞅,是把js保存下來然后使用phantomjs調用加密函數對數據進行加密。
登錄時抓包查看到數據包如下,可以看到passwd字段進行了明顯的加密
一般密碼加密會在登錄事件進行加密處理,那么我們從這里開始跟蹤,選中登錄按鈕右鍵檢查元素定位。
可以看有一個checkusernew()函數,轉到調試器,直接ctrl+sgift+f全局搜索這個函數。
然后轉到函數定義處,直接點擊右側加個斷點
然后重新走登錄流程,自動運行到斷點處,然后F10 不過、F11 步入、shift+F11 步出。
對關鍵函數如hex_md5和Encrypt需要跟進去看下做了什么。
跟進encrypt函數
我們需要去跟進獲取加密的key和iv么,當然是不需要的,我們只需要在控制臺調用執行即可
但是如何批量呢?有兩種方案,一個直接在控制臺寫js代碼或者拉取相應JS,調用python的PyExecJS模塊。
我更傾向于直接在控制臺調用
let arr = ['a', 'b', 'c', 'd', 'e'] //定義一個列表
var a = "" //定義一個空字符串
for (let i in arr) {a = a + Encrypt(hex_md5(arr[i])) + "\n"} //循環跑
最后輸出字符a即可
在測試過程遇到的情況往往可能要復雜得多,跟了半天都沒有跟到關鍵函數也有可能發生。再加上web前端項目打包導致函數更加不好找。
這個時候就要果斷放棄,打開下一個。
Web標準構成
主要包括:結構、表現和行為三個方面
<!DOCTYPE>文檔類型聲明,作用就是告訴瀏覽器使用哪種HTML版本來顯示網頁;
lang用來定義當前文檔顯示的語言;
字符集(cahracter set)是多個字符的集合,以便計算機能夠識別和存儲各種文字;
charset常用的值有:GB2312,BIG5,GBK和UTF-8,其中UTF-8也被稱為萬國碼,基本包含了全世界所有國家需要用到的字符集。
HTML常用標簽
標題標簽:<h1></h1>,<h2></h2>,<h3></h3>,<h4></h4>,<h5></h5>,<h6></h6>(字體大小依次來變小)
段落標簽:<p></p>
換行標簽:<br/>
加粗標簽:<strong></strong>或<b></b>(推薦使用strong標簽,因為atrong效果更好)
傾斜標簽:<em></em>;<i></i>(推薦使用em標簽,因為em效果更好)
刪除線標簽: <del></del>或<s></s>(推薦使用del標簽,因為del效果更好)
下劃線標簽:<ins></ins>或<u></u>(推薦使用ins標簽,因為ins效果更好)
div和span標簽:是一個盒子,用來裝內容,div是塊級標簽,span是行內標簽
圖像標簽:<img src="圖像 URL"/>
水平線標記:<hr/>
圖像路徑
相對路徑:以引用文件所在位置為參考基礎,而建立出的目錄路徑
絕對路徑:是指目錄下的絕對位置,直接到達目標位置,通常是從盤符開始的路徑
超鏈接標簽:<a href="跳轉地址" target="目標窗口的彈出方式"> </a>作用從一個頁面跳轉到另外一個頁面
描點連接:配合id選擇器使用
注釋:<!-- --!>
/**/
//
特殊字符: (空格)等
表格標簽:<table></table> 作用于展示數據
<th></th>----表頭單元格
<tr></tr> ----行
<td></td>----單元格,列
row="行數" cols="每行的字數"
<thead></thead>標簽用于表格的頭部區域;<tbody></tbody>標簽用于表格的主題區域
無序列表:<ul><li></li></ul>
有序列表:<ol><li></li></ol>
自定義列表:<dl><dt><dd></dd></dt></dl>-----------dt是用于標題,dd是內容
表單的組成:
在HTML中,表單通常由表單域、表單控件和表單信息
from用于定義表單域,以實現用戶信息的收集和傳遞
表單域標簽:<from action="url地址" method="提交方式" name="表單域名稱"></form>
<input type="屬性值"/>
其中name屬性是表單的名字,相同的名字可以實現多選一
<lable>標簽:用于綁定一個表單元素,當點擊《lable》標簽內的文本時,瀏覽器就會自動將焦點(光標)轉到或者選擇對應的表單元素上,用來增加用戶體驗
<select>表單元素:下拉表單元素,
<select>
<option></option>
</select>
select中至少包含一對option
textarea表單元素:<textarea></textarea>-----文本域標簽
做登錄界面可以用表格來做,能直接對齊
后續會更新后面的知識
自己是一名從事了多年開發的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年年初我花了一個月整理了一份最適合2019年學習的web前端學習干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關注我的頭條號并在后臺私信我:前端,即可免費獲取。
函數是什么
函數是完成某個特定功能的一組語句。如沒有函數,完成任務可能需要五行、十行、甚至更多的代碼。這時我們就可以把完成特定功能的代碼塊放到一個函數里,直接調用這個函數,就省重復輸入大量代碼的麻煩。
函數可以概括為:一次封裝,四處使用。
函數的定義
函數的定義方式通常有三種:函數聲明方式、函數表達式、 使用Function構造函數 。
函數聲明方式
語法:
function 函數名(參數1,參數2,...){ //要執行的語句 }
例:
// 聲明 function sum(num1, num2) { return num1 + num2; } // 調用 sum(1, 2) // 3
函數表達式
語法:
var fn = function(參數1,參數2,...){ //要執行的語句 };
例:
// 聲明 var sum = function(num1,num2){ return num1+num2; }; // 調用 sum(1, 2) // 3
使用Function構造函數
Function構造函數可以接收任意數量的參數,最后一個參數為函數體,其他的參數則枚舉出新函數的參數。其語法為:
new Function("參數1","參數2",...,"參數n","函數體");
例:
// 聲明 var sum = new Function("num1","num2","return num1+num2"); // 調用 sum(1, 2) // 3
三種定義方式的區別
三種方式的區別,可以從作用域、效率以及加載順序來區分。
從作用域上來說,函數聲明式和函數表達式使用的是局部變量,而 Function()構造函數卻是全局變量,如下所示:
var name = '我是全局變量 name'; // 聲明式 function a () { var name = '我是函數a中的name'; return name; } console.log(a()); // 打印: "我是函數a中的name" // 表達式 var b = function() { var name = '我是函數b中的name'; return name; // 打印: "我是函數b中的name" } console.log(b()) // Function構造函數 function c() { var name = '我是函數c中的name'; return new Function('return name') } console.log(c()()) // 打印:"我是全局變量 name",因為Function()返回的是全局變量 name,而不是函數體內的局部變量。
從執行效率上來說,Function()構造函數的效率要低于其它兩種方式,尤其是在循環體中,因為構造函數每執行一次都要重新編譯,并且生成新的函數對象。
來個例子:
var start = new Date().getTime() for(var i = 0; i < 10000000; i++) { var fn = new Function('a', 'b', 'return a + b') fn(i, i+1) } var end = new Date().getTime(); console.log(`使用Function構造函數方式所需要的時間為:${(end - start)/1000}s`) // 使用Function構造函數方式所需要的時間為:8.646s start = new Date().getTime(); var fn = function(a, b) { return a + b; } for(var i = 0; i < 10000000; i++) { fn(i, i+1) } end = new Date().getTime(); console.log(`使用表達式的時間為:${(end - start)/1000}s`) // 使用表達式的時間為:0.012s
由此可見,在循環體中,使用表達式的執行效率比使用 Function()構造函數快了很多很多。所以在 Web 開發中,為了加快網頁加載速度,提高用戶體驗,我們不建議選擇 Function ()構造函數方式來定義函數。
最后是加載順序,function 方式(即函數聲明式)是在 JavaScript 編譯的時候就加載到作用域中,而其他兩種方式則是在代碼執行的時候加載,如果在定義之前調用它,則會返回 undefined:
console.log(typeof f) // function console.log(typeof c) // undefined console.log(typeof d) // undefined function f () { return 'JS 深入淺出' } var c = function () { return 'JS 深入淺出' } console.log(typeof c) // function var d = new Function('return "JS 深入淺出"') console.log(typeof d) // function
函數的參數和返回值
函數的參數-arguments
JavaScript 中的函數定義并未指定函數形參的類型,函數調用也未對傳入的實參值做任何類型檢查。實際上,JavaScript 函數調用甚至不檢查傳入形參的個數。
function sum(a) { return a + 1; } console.log(sum(1)); // 2 console.log(sum('1')); // 11 console.log(add()); // NaN console.log(add(1, 2)); // 2
當實參比形參個數要多時,剩下的實參沒有辦法直接獲得,需要使用即將提到的arguments對象。
JavaScript中的參數在內部用一個數組表示。函數接收到的始終都是這個數組,而不關心數組中包含哪些參數。在函數體內可以通過arguments對象來訪問這個參數數組,從而獲取傳遞給函數的每一個參數。arguments對象并不是Array的實例,它是一個類數組對象,可以使用方括號語法訪問它的每一個元素。
function sum (x) { console.log(arguments[0], arguments[1], arguments[2]); // 1 2 3 } sum(1, 2, 3)
arguments對象的length屬性顯示實參的個數,函數的length屬性顯示形參的個數。
function sum(x, y) { console.log(arguments.length); // 3 return x + 1; } sum(1, 2, 3) console.log(sum.length) // 2
函數的參數-arguments
JavaScript 中的函數定義并未指定函數形參的類型,函數調用也未對傳入的實參值做任何類型檢查。實際上,JavaScript 函數調用甚至不檢查傳入形參的個數。
function sum(a) { return a + 1; } console.log(sum(1)); // 2 console.log(sum('1')); // 11 console.log(add()); // NaN console.log(add(1, 2)); // 2
函數的參數-同名參數
在非嚴格模式下,函數中可以出現同名形參,且只能訪問最后出現的該名稱的形參。
function sum(x, x, x) { return x; } console.log(sum(1, 2, 3)) // 3
而在嚴格模式下,出現同名形參會拋出語法錯誤。
function sum(x, x, x) { 'use strict'; return x; } console.log(sum(1, 2, 3)) // SyntaxError: Duplicate parameter name not allowed in this context
函數的參數-參數個數
當實參比函數聲明指定的形參個數要少,剩下的形參都將設置為undefined值。
function sum(x, y) { console.log(x, y); } sum(1); // 1 undefined
函數的返回值
所有函數都有返回值,沒有return語句時,默認返回內容為undefined。
function sum1 (x, y) { var total = x + y } console.log(sum1()) // undefined function sum2 (x, y) { return x + y } console.log(sum2(1, 2)) // 3
如果函數調用時在前面加上了new前綴,且返回值不是一個對象,則返回this(該新對象)。
function Book () { this.bookName = 'JS 深入淺出' } var book = new Book(); console.log(book); // Book { bookName: 'JS 深入淺出' } console.log(book.constructor); // [Function: Book]
如果返回值是一個對象,則返回該對象。
function Book () { return {bookName: JS 深入淺出} } var book = new Book(); console.log(book); // { bookName: 'JS 深入淺出' } console.log(book.constructor); // [Function: Book]
函數的調用方式
JS 一共有4種調用模式:函數調用、方法調用、構造器調用和間接調用。
函數調用
當一個函數并非一個對象的屬性時,那么它就是被當做一個函數來調用的。對于普通的函數調用來說,函數的返回值就是調用表達式的值
function sum (x, y) { return x + y; } var total = sum(1, 2); console.log(total); // 3
使用函數調用模式調用函數時,非嚴格模式下,this被綁定到全局對象;在嚴格模式下,this是undefined
// 非嚴格模式 function whatIsThis1() { console.log(this); } whatIsThis1(); // window // 嚴格模式 function whatIsThis2() { 'use strict'; console.log(this); } whatIsThis2(); // undefined
方法調用
當一個函數被保存為對象的一個屬性時,稱為方法,當一個方法被調用時,this被綁定到該對象。
function printValue(){ console.log(this.value); } var value=1; var myObject = {value:2}; myObject.m = printValue; //作為函數調用 printValue(); //作為方法調用 myObject.m();
咱們注意到,當調用printValue時,this綁定的是全局對象(window),打印全局變量value值1。但是當調用myObject.m()時,this綁定的是方法m所屬的對象Object,所以打印的值為Object.value,即2。
構造函數調用
如果函數或者方法調用之前帶有關鍵字new,它就構成構造函數調用。
function fn(){ this.a = 1; }; var obj = new fn(); console.log(obj.a);//1
參數處理:一般情況構造器參數處理和函數調用模式一致。但如果構造函數沒用形參,JavaScript構造函數調用語法是允許省略實參列表和圓括號的。
如:下面兩行代碼是等價的。
var o = new Object(); var o = new Object;
函數的調用上下文為新創建的對象。
function Book(bookName){ this.bookName = bookName; } var bookName = 'JS 深入淺出'; var book = new Book('ES6 深入淺出'); console.log(bookName);// JS 深入淺出 console.log(book.bookName);// ES6 深入淺出 Book('新版JS 深入淺出'); console.log(bookName); // 新版JS 深入淺出 console.log(book.bookName);// ES6 深入淺出
1.第一次調用Book()函數是作為構造函數調用的,此時調用上下文this被綁定到新創建的對象,即 book。所以全局變量bookName值不變,而book新增一個屬性bookName,值為'ES6 深入淺出';
2.第二次調用Book()函數是作為普通函數調用的,此時調用上下為this被綁定到全局對象,在瀏覽器中為window。所以全局對象的bookNam值改變為' 新版JS 深入淺出',而book的屬性值不變。
間接調用
JS 中函數也是對象,函數對象也可以包含方法,call()和apply()方法可以用來間接地調用函數。
這兩個方法都允許顯式指定調用所需的this值,也就是說,任何函數可以作為任何對象的方法來調用,哪怕這個函數不是那個對象的方法。兩個方法都可以指定調用的實參。call()方法使用它自有的實參列表作為函數的實參,apply()方法則要求以數組的形式傳入參數。
var obj = {}; function sum(x,y){ return x+y; } console.log(sum.call(obj,1,2));//3 console.log(sum.apply(obj,[1,2]));//3
詞法(靜態)作用域與動態作用域
作用域
通常來說,一段程序代碼中所用到的名字并不總是有效/可用的,而限定這個名字的可用性的代碼范圍就是這個名字的作用域。
詞法作用域
詞法作用域,也叫靜態作用域,它的作用域是指在詞法分析階段就確定了,不會改變。而與詞法作用域相對的是動態作用域,函數的作用域是在函數調用的時候才決定的。
來個例子,如下代碼所示:
var blobal1 = 1; function fn1 (param1) { var local1 = 'local1'; var local2 = 'local2'; function fn2(param2) { var local2 = 'inner local2'; console.log(local1) console.log(local2) } function fn3() { var local2 = 'fn3 local2'; fn2(local2) } fn3() } fn1()
當瀏覽器看到這樣的代碼,不會馬上去執行,它會先生成一個抽象語法樹。上述代碼生成的抽象語法樹大概是這樣的:
執行fn1函數,fn1中調用 fn3(),從fn3函數內部查找是否有局部變量 local1,如果沒有,就根據抽象樹,查找上面一層的代碼,也就是 local1 等于 'local1' ,所以結果會打印 'local1'。
同樣的方法查找是否有局部變量 local2,發現當前作用域內有local2變量,所以結果會打印 'inner local2。
思考
有如下的代碼:
var a = 1; function fn() { console.log(a) }
兩個問題:
對于第一個問題:
分析一個語法,就能確定函數 fn里面的 a 就是外面的 a。
對于第二個問題:
函數 fn 里面的變量 a的值, 不一定是外面的變量 a的值,假設咱們這樣做:
var a = 1; function fn() { console.log(a) } a = 2 fn()
這時候當咱們執行 fn() 的時候,打印 a 的值為 2。所以如果沒有看到最后,一開始咱們是不知道打印的 a 值到底是什么。
所以詞法作用域只能確定變量所在位置,并不能確定變量的值。
調用棧(Call Stack)
什么是執行上下文
執行上下文就是當前JavaScript代碼被解析和執行是所在環境的抽象概念,JavaScript中運行任何的代碼都是在執行上下文中運行。
執行上下文的類型,主要有兩類:
調用棧
調用棧,具有LIFO(Last in, First out 后進先出)結構,用于存儲在代碼執行期間創建的所有執行上下文。
當JavaScript引擎首次讀取腳本時,會創建一個全局執行上下文并將其push到當前執行棧中。每當發生函數調用時,引擎都會為該函數創建一個新的執行上下文并push到當前執行棧的棧頂。
引擎會運行執行上下文在執行棧棧頂的函數,根據LIFO規則,當此函數運行完成后,其對應的執行上下文將會從執行棧中pop出,上下文控制權將轉到當前執行棧的下一個執行上下文。
看看下面的代碼:
var myOtherVar = 10; function a() { console.log('myVar', myVar); b(); } function b() { console.log('myOtherVar', myOtherVar); c(); } function c() { console.log('Hello world!'); } a(); var myVar = 5;
有幾個點需要注意:
當它被執行時你期望發生什么?是否發生錯誤,因為b在a之后聲明或者一切正常?console.log打印的變量又是怎么樣?
以下是打印結果:
"myVar" undefined "myOtherVar" 10 "Hello world!"
1. 變量和函數聲明(創建階段)
第一步是在內存中為所有變量和函數分配空間。但請注意,除了undefined之外,尚未為變量分配值。因此,myVar在被打印時的值是undefined,因為JS引擎從頂部開始逐行執行代碼。
函數與變量不一樣,函數可以一次聲明和初始化,這意味著它們可以在任何地方被調用。
所以以上代碼在創建階段時,看起來像這樣子:
var myOtherVar = undefined var myVar = undefined function a() {...} function b() {...} function c() {...}
這些都存在于JS創建的全局上下文中,因為它位于全局作用域中。
在全局上下文中,JS還添加了:
2. 執行
接下來,JS 引擎會逐行執行代碼。
myOtherVar = 10在全局上下文中,myOtherVar被賦值為10
已經創建了所有函數,下一步是執行函數 a()
每次調用函數時,都會為該函數創建一個新的上下文(重復步驟1),并將其放入調用堆棧。
function a() { console.log('myVar', myVar) b() }
如下步驟:
下面調用堆棧的執行示意圖:
創建全局上下文,全局變量和函數。
來源:https://mp.weixin.qq.com/s/4JmQTcvXBew8Eiz8hkYsWQ
作者:前端小智
*請認真填寫需求信息,我們會在24小時內與您取得聯系。