二維碼的秘密


二維碼的秘密

最近在做一個項目,需要做App掃碼登錄,前端利用websocket,監聽后端掃碼成功的響應來實現登錄。我用了qrcodejs來生成二維碼,二維碼生成出來的時候,我有一些疑惑:

  • 它的容量有多大?
  • 二維碼里面三個大方塊是干什么的?
  • 能存儲視頻/音頻嗎?
  • 破損了為什么還能被識別?
  • 生成原理是怎樣的?

帶着這些疑問,查閱相關資料,得到了我想要的答案

誕生和普及

上個世紀60年代,日本迎來高速增長期,賣食品、衣服等種類繁多的超市開始在城市中出現,當時超市使用的現金出納機要靠手動輸入商品價格,因此負責現金出納的人常常會因手腕的麻木和“腱鞘炎”而苦惱。

技術是第一生產力,條形碼的出現解決了這個問題,因此,條形碼得以普及,條形碼也叫一維碼,缺點是,它的容量非常有限,最多能容納20個英文字符和數字。怎么辦?有個日本人,他叫原昌宏,投入了二維碼的研發,一年半后,幾經曲折,二維碼誕生了。

二維碼共有40個版本,每個版本有固定的碼元數。碼元是什么?就是位,1碼元等於1bits。第一個版本(version 1)的碼元數為21碼元×21碼元,version 2的碼元數為25碼元×25碼元,每個版本橫向和縱向各自以4碼元為單位遞增,以此類推

一維碼和二維碼

二維碼翻譯成英文為QR Code,全稱為Quick Response Code,即快速響應碼,二維碼研發之初,有兩個重點,一個是可以容納大量信息,另一個就是可以快速讀取。二維碼跟一維碼比起來,優點顯而易見,以下是兩者之間的差異

類型 差異
一維碼 1、容量較小:30個字符左右
2、只能包含字母和數字
3、尺寸相對較大(空間利用率低)
4、遭到破壞后無法讀取
二維碼 1、數據容量大
2、超越了數字字母的限制
3、尺寸相對較小
4、被破壞依然可以被讀取

二維碼圖形拆解

在繼續深入了解二維碼之前,先觀察二維碼圖片,了解一些基本概念

二維碼左上角、右上角以及左下角都有一個回形方塊,右下方有一個小的回形方塊,他們的作用是什么?剩下的都是一些類似於像素點的黑白方塊,他們是怎么來的?

三個大方塊,即位置探測圖形,用來標記二維碼的大小和方向
定位圖形:二維碼圖形過大時,掃碼容易畸形,定位圖形就來防止畸形的產生
校正圖形:版本2及以上才有
格式信息:包括糾錯等級、掩碼類別
版本信息:二維碼的版本
數據和糾錯碼字:數據碼和糾錯碼

出現了一些新概念,暫時不理解不要緊,往下看

二維碼優點

1、存儲大容量信息

一圖勝千言,一個小小的二維碼,可以存儲成百上千個字符,具體容量跟二維碼的版本有關,版本越大,容量越大
最小的版本1能存儲41個數字 || 25個英文+數字 || 10個漢字
最大的版本40能存儲7089個數字 || 4296個英文+數字 || 1817個漢字

2、在小空間內打印

相對而言,二維碼占用的空間比一維碼小得多

3、有效處理各種文字

二維碼可以存儲各種文字,因為最終都是轉換成二進制

4、具備糾錯能力

二維碼小部分破損,仍然可以被讀取,因為二維碼有糾錯能力,糾錯能力還分為4個級別,分別是:

  • L級,恢復率7%
  • M級,恢復率15%
  • Q級,恢復率25%
  • H級,恢復率30%

級別越高,糾錯能力越強,但由於數據量會隨之增加,二維碼尺寸也會變大。
糾錯級別的恢復率,是指全部碼字與可以糾錯的碼字的比例。例如,一共有100個碼字(數據碼),要對其中50個進行糾錯,糾錯級別為Q,則需要糾錯碼的個數等於50/0.25 - 100 = 100,也就是需要100個糾錯碼,加上數據碼一共200個碼字。

碼字是什么概念?1個碼字 = 8個碼元,一個碼元 = 1bits

5、360度方向讀取

二維碼可以從任何角度讀取

6、支持數據合並

一個二維碼可以拆分為多個,多個也可以合並為一個

安全性

2018年9月,出現了一些二維碼,iPhone掃碼后,立刻重啟,這是因為二維碼指向一個包含成千上萬個div,設置了特定的樣式,耗盡了iPhone的資源,而觸發了iPhone的自我保護機制,重啟了。這說明二維碼帶來便利的同時,也存在一些安全問題。

掃描二維碼可以跳轉到指定URL,是因為URL轉換為二進制存儲在二維碼里面,因此所有基於URL的攻擊,都有可能存在二維碼中,什么網絡釣魚、傳播惡意軟件、SQL注入、XSS之類,甚至可能二維碼本身就是惡意URL編碼生成的,也可能是正常的二維碼生成后被篡改,針對前者,要做的就是不去掃描來源不明的二維碼,針對后者,解決方式無外乎兩種:

第一種:非對稱加密,二維碼生產者用私鑰加密,解碼工具用生產者的公鑰解密,類似於證書校驗。
第二種:將二維碼生成hash值,和URL一起編碼到二維碼內容里,解碼工具將掃描到的二維碼內容生成hash,與二維碼內自帶的hash比對一致,意味着二維碼沒有被篡改過。

二維碼生成原理

生成原理,重點是數據碼和糾錯碼的生成

數據碼的生成

先上4個表

表1,模式編號指示器

Mode Indicator
數字 0001
字母數字 0010
8位字節 0100
日文 1000
中文 1101
…… ……

表2,字符計數指示器中的位數

表3,字符映射表

表4,二維碼版本對應的碼字數(第2列)和糾錯碼數(第4列)

案例:將數字01234567編碼,指定版本為1,糾錯級別為Q

1、將數字3位為1組,分為3組:012,345,67

2、分別將3組數字轉換成二進制,二進制長度為10(查表2,可知版本1-9,數字位數為10),012轉成0000001100,345轉成0101011001,67轉成1000011,最后的67轉換的長度為什么不是10位而是7位?分組后,不足3位的組,轉換為4位或7位

3、把數字的個數轉成二進制:01234567長度為8,8的二進制是0000001000,長度同樣是10位

4、把數字編碼的標志0001(查表1可得)和第3步的編碼加到前面,得到如下編碼

模式編號 字符數 數據編碼
0001 0000001000 0000001100 0101011001 1000011

5、在末尾加上結束符,結束符固定為4個0,得到如下編碼

模式編號 字符數 數據編碼 結束符
0001 0000001000 0000001100 0101011001 1000011 0000

如果編碼長度不是8的倍數,需要在后面繼續補0,目前一共是83bits,需要補5個0,得到

模式編號 字符數 數據編碼 結束符 不是8的倍數補0
0001 0000001000 0000001100 0101011001 1000011 0000 00000

6、未達到最大bits數限制(每個版本都有最大bits上限,查表4可得,版本1,糾錯級別Q,總碼字數26,糾錯碼占了13個碼字,那么數據碼為26 - 13 = 13個碼字,最大bits數限制為13*8 = 104bits),末尾加補齊碼,重復11101100 00010001直到達到最大限制,得到

模式編號 字符數 數據編碼 結束符 不是8的倍數補0 補齊碼
0001 0000001000 0000001100 0101011001 1000011 0000 00000 11101100 00010001

以上編碼即為數字01234567的數據碼,字符(英文+數字)的編碼略有不同,相同的是編碼都很繁瑣

糾錯碼的生成

糾錯碼,使得有些二維碼污損了也能掃碼解析,主要是通過里德-所羅門糾錯算法(Reed-Solomon Error Correction)實現的,這個算法,比較復雜,感興趣的可以看看相關鏈接

二維碼繪制

有了數據碼和糾錯碼,就可以開始繪制二維碼了,對照上文的二維碼拆解圖,繪制分為以下幾步

1、 位置探測圖形:不管二維碼版本是多少,位置探測圖形固定為7×7碼元

2、 定位圖形

3、 校正圖形:不管二維碼版本是多少,位置探測圖形固定為5×5碼元

4、 格式信息,包括糾錯等級、掩碼類別:格式信息固定為15bits,其中5bits數據位,10bits糾錯位

  • 5bits數據位中,2bits表示糾錯等級,3bits表示掩碼類別

糾錯等級編碼表

糾錯等級 二進制編碼
L 01
M 00
Q 11
H 10

掩碼,是在填充完數據碼和糾錯碼之后,和數據區(包括數據碼和糾錯碼)進行異或運算,讓二維碼中黑色塊和白色塊分布得更均勻一些,便於解碼

掩碼類別共有8種,二進制編碼分別是000、001、010、011、100、101、110、111,下面的圖片印刷有誤,最后兩種編碼寫成一樣,計算表達式也寫成一樣,實際最后一個編碼為111,計算表達式為 ((ij)mod 3 + (i + j)mod 2) mod 2 = 0,很多文章都沒有糾正這一點

5、版本信息:版本7及以上的二維碼才需要加入版本信息

6、填充數據碼和糾錯碼:數據碼和糾錯碼都是一串長長的二進制數,每8bits為一塊,即一個碼字,填充順序從右下角往上逐個填充,到頂后再從上往下填充,以此類推,如下圖

以下是一個版本2,糾錯級別為M的二維碼數據區示意圖,先填充數據碼,數據碼填充完再填充糾錯碼,D開頭的是數據碼,E開頭為糾錯碼

7、掩碼:第4點講到過,填充完數據區后,黑白點可能不均衡,會有大面積空白或黑色塊,解碼困難,掩碼就是用來解決這個問題。將數據區的二進制數,和掩碼的二進制數進行異或運算,最終得到的圖形,才是我們看到的二維碼。

結語

回到本文開頭,還有一個問題:二維碼能存儲視頻/音頻嗎?答案當然是可以的,因為最終都是轉換成二進制,但是,二維碼容量實際只有1KB,要存儲視頻/音頻,只能說理論上可以。

覺得不錯,點個star吧Github


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM