Xây dựng trình kiểm tra đơn giản độ mạnh của mật khẩu

Cung cấp thông tin phản hồi ngay lập tức là điều ngay bây giờ. Tại sao lại giới hạn bản thân trong việc kiểm tra tên người dùng và địa chỉ email? Tại sao không mở rộng điều này để cung cấp phản hồi trực quan nhanh về độ mạnh của mật khẩu mà người dùng đã nhập? Hôm nay, chúng ta sẽ xem làm sao để tạo một trình kiểm tra độ mạnh mật khẩu đơn giản bằng thư viện jQuery, cùng các biểu thức thông thường và một thuật toán đơn giản.

Vài lời từ tác giác

Như hầu hết các chuyên gia bảo mật sẽ nói với bạn rằng người dùng luôn là liên kết yếu nhất. Phần bảo mật nhất của hệ thống có thể bị tấn công khi người dùng chọn mật khẩu cực kỳ khó hiểu. Với ý nghĩ đó, xu hướng gần đây dường như đang cung cấp phản hồi nhanh chóng cho người dùng về độ mạnh của mật khẩu để người dùng có thể mở rộng hoặc sửa đổi mật khẩu nhằm bảo mật hơn.

Hovers

Hôm nay, chúng ta sẽ sử dụng thư viện jQuery, một loạt các biểu thức thông thường và một thuật toán rất đơn giản để tạo một trình kiểm tra cơ bản cho độ mạnh mật khẩu. Có hứng thú không? Hãy bắt đầu ngay lập tức! Dưới đây là bản demo về điều chúng tôi đang cố gắng xây dựng hôm nay:

Demo image

Các mục tiêu thiết kế

Mục tiêu thiết kế của chúng tôi cho chức năng cụ thể này là tương đối nhỏ.

  • Đưa ra phản hồi trực quan cho người dùng về độ mạnh của mật khẩu của họ.
  • Phải ngay lập tức nhận được phản hồi. Điều này có nghĩa là không nhấp vào nút để kiểm tra sức mạnh.
  • Sự kiện kích hoạt có thể là bất kỳ sự kiện nào trong bàn phím. Tôi đã chọn keyup vì nó phù hợp nhất cho nhu cầu cụ thể của chúng tôi.
  • Đối với phản hồi trực quan, sửa đổi văn bản riêng rẻ khi hữu ích thì hoàn toàn thiếu sót. Tôi cũng để chọn thay đổi màu nền để thu hút sự chú ý của người dùng về điều này.
  • Cung cấp thêm thông tin phản hồi có thể định lượng để người dùng biết sức mạnh mật khẩu thiếu sót ở phần nào và làm sao để cải thiện.

Bây giờ chúng tôi đã tìm ra đầy đủ nhu cầu của mình, chúng tôi có thể chuyển sang giai đoạn tiếp theo.

<figure><iframe src="https://b4a5b896ac66291eba3c2ac4dc9b4f4b.safeframe.googlesyndication.com/safeframe/1-0-37/html/container.html" width="0" height="283"></iframe></figure>

Advertisement

Kế hoạch cho hành động

Bây giờ chúng ta sẽ quyết định thứ tự của các bước riêng lẻ cần phải được thực hiện.

  • Kết nối trình xử lý sự kiện với sự kiện keyup của hộp nhập dữ liệu.
  • Hãy để trình xử lý sự kiện kiểm tra dữ liệu dữ liệu nhập vào nhưng ủy thác mọi thứ khác cho các phương thức helper riêng lẻ.
  • Các phương thức helper sẽ phân tích cú pháp dữ liệu dữ liệu nhập vào và phân tích nó, tính toán độ phức tạp và in ra kết quả.
  • Đảm bảo rằng các trình xử lý sự kiện chỉ gửi đi các phương thức helper nếu độ dài của dữ liệu dữ liệu nhập vào lớn hơn mức tối thiểu dự kiến để không lãng phí chu kỳ CPU vào các mục không hợp lệ.
  • Trả lại quyền điều khiển cho trình xử lý sự kiện trong trường hợp điều gì khác cần hoàn tất.

Thuật toán

The Algorithm

Để giữ cho bài viết này ngắn gọn và dễ tiếp cận, tôi đã quyết định dùng một thuật toán rất cơ bản. Thuật toán phân tích chuỗi, đưa ra các phần thưởng cho độ dài bổ sung, sự hiện diện của các con số, ký hiệu và chữ in hoa và phạt cho dữ liệu dữ liệu nhập vào chữ cái hoặc con số. Chúng tôi sẽ không xem xét việc khớp các pattern thông dụng hoặc kiểm tra dữ liệu nhập vào so với từ điển vì điều này nằm ngoài phạm vi của bài viết. Nếu thực sự quan tâm, thì tôi có thể thực hiện một bài viết về điều này sau dài.

Đầu tiên chúng ta kiểm tra độ dài của chuỗi dữ liệu nhập vào. Nếu nó lớn hơn độ dài tối thiểu, hãy đánh điểm số cơ bản là 50. Nếu kết quả khác thì điểm là 0. Tiếp theo lặp qua từng ký tự của chuỗi và kiểm tra xem đó là ký hiệu, số hoặc chữ in hoa. Nếu vậy, hãy ghi chú về ký tự đó.

Sau đó kiểm tra xem chuỗi có bao nhiêu ký tự, vượt quá mức tối thiểu được đề xuất và cấp phần thưởng cho mỗi ký tự. Đồng thời cấp phần thưởng nếu chuỗi chứa kết hợp các chữ cái in hoa, số và ký hiệu hoặc cả ba. Cấp tiền thưởng cho sự hiện diện của mỗi thứ.

Kiểm tra xem chuỗi chỉ chứa chữ cái hoặc chữ thường và nếu có, sẽ bị phạt.

Thêm tất cả các số và quyết định độ mạnh của mật khẩu phù hợp.

Đó là độ dài và ngắn của thuật toán. Sẽ không quá phức tạp nhưng nó loại được rất nhiều mật khẩu xấu. Bạn sẽ hiểu điều này nhiều hơn khi chúng ta thấy bằng code.

Core markeup

Markup cho HTML của trang demo trông giống như vậy:

<!DOCTYPE html>
<html lang="en-GB">
<head>
<title>Simple Password Strength Checker - by Siddharth for NetTuts</title>
<link type="text/css" href="css/style.css" rel="stylesheet" />
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/mocha.js"></script>
</head>
<body>
 
<div id="container">
 
<h1>Create a simple password strength checker</h1>
 
<h2 class="bolded">by Siddharth for the lovely folks at Net Tuts</h2>
 
<p>Type in your password to get visual feedback regarding the strength of your password.</p>
<p>I assure you, I am not stealing your passwords. The form doesn't not submit. You can look through the source if you are suspicious. :)</p> 
 
<div class="block">
<input id="inputPassword"/>
<div id="complexity" class="default">Enter a random value</div>
</div>
 
<div class="block">
<div id="results" class="default">Breakdown of points</div>
<div id="details"></div>
</div>
 
</div>
</body>
</html>

Bỏ qua tất cả các markup thông thường. Hãy để ý phần tử input có ID là inputPassword, phần tử div có ID tên complexity cho thấy độ phức tạp của mật khẩu và phần tử div có ID tên details để hiển thị bảng phân tích điểm.

Chúng tôi cũng đã bao gồm thư viện jQuery và file script riêng của chúng tôi. Điểm cộng thêm nếu bạn đánh giá cao tên của file script của chúng tôi.

Tạo kiểu cho CSS

body{
  font-family: "Lucida Grande", "Verdana", sans-serif;
}
 
h1{
    font-size: 30px;
    padding: 0;
    margin: 0;
}
 
h2{
    font-size: 18px;
    padding: 0;
    margin: 0 5px 30px 0;
}
 
input{
    width: 288px;
    height: 30px;
    margin: 50px 0 0 0;
    padding: 3px 5px;
    font-size: 22px;
    font-family: "Lucida Grande", "Verdana", sans-serif;
}
 
#container{
    width: 820px;
    margin-left: auto;
    margin-right: auto;
    padding: 50px 0 0 0;
}
 
.block{
    width: 300px;
    margin: 0 auto 0 auto;
}
 
#complexity, #results{
    width: 300px;
    padding: 3px 0;
    height: 20px;
    color: #000;
    font-size: 14px;
    text-align: center;
}
 
#results{
    margin: 30px 0 20px 0;
}
 
.default{background-color: #CCC;}
.weak{background-color: #FF5353;}
.strong{background-color: #FAD054;}
.stronger{background-color: #93C9F4; }
.strongest{background-color: #B6FF6C;}
 
span.value{
    font-weight:bold;
    float: right;
}

Chỉ cần dùng mẫu sẵn CSS cho bố cục và kiểu chữ. Chúng tôi có một loạt các class ở dưới cùng cho các xếp hạng sức mạnh cá nhân. Chúng tôi sẽ bổ sung chúng vào các yếu tố khi cần thiết.

Triển khai JavaScript

Bây giờ chúng ta có một framework vững chắc và một số style cơ bản, chúng ta có thể bắt đầu code các chức năng cần thiết. Xin lưu ý rằng chúng tôi sử dụng nhiều jQuery. Vui lòng liên kết với CDN của Google nếu cần thiết.

Các biến và xử lý sự kiện

Vì rất nhiều trò tung hứng sẽ diễn ra, chúng ta cần một loạt các biến để giữ các giá trị. Vì là bản demo và không phải code thực tế, tôi đã quyết định khai báo các biến là toàn cục và truy xuất chúng thông qua các phương thức của helper thay vì khai báo chúng trong nội bộ và sau đó truyền nó đến các hàm.

var strPassword;
var charPassword;
var complexity = $("#complexity");
var minPasswordLength = 8;
var baseScore = 0, score = 0;
 
var num = {};
num.Excess = 0;
num.Upper = 0;
num.Numbers = 0;
num.Symbols = 0;
 
var bonus = {};
bonus.Excess = 3;
bonus.Upper = 4;
bonus.Numbers = 5;
bonus.Symbols = 5;
bonus.Combo = 0; 
bonus.FlatLower = 0;
bonus.FlatNumber = 0;

Tên biến là giá vé khá tiêu chuẩn nhưng dù sao tôi cũng sẽ đưa ra một danh sách. strPassword lưu giá trị của dữ liệu nhập vào, charPassword là một mảng chứa từng ký tự của chuỗi, complexity giữ một tham chiếu đến phần tử div. Chúng tôi cũng xác định độ dài tối thiểu của mật khẩu, điểm và điểm cơ sở.

Chúng tôi tạo ra một hash nhanh để lưu số lượng ký tự bổ sung, ký tự chữ hoa, số và ký hiệu. Chúng tôi làm tương tự cho các bonus. Hash tên num lưu số lượng ký tự trong khi hash tên bonus lưu bội số của bonus. Bạn chỉ có thể tạo các biến riêng lẻ nhưng tôi nghĩ rằng điều này có gọn gàng hơn.

Đừng quên kết nối xử lý sự kiện với sự kiện.

1$("#inputPassword").bind("keyup", checkVal);

checkVal là trình xử lý sự kiện mà chúng ta sẽ tạo chỉ trong giây lát.

Trình xử lý sự kiện

function checkVal()
{
    if (charPassword.length >= minPasswordLength)
    {
        baseScore = 50; 
        analyzeString();    
        calcComplexity();       
    }
    else
    {
        baseScore = 0;
    }
     
    outputResult();
}

Trước tiên chúng tôi kiểm tra độ dài của chuỗi dữ liệu nhập vào. Nếu nó lớn hơn hoặc bằng độ dài tối thiểu được chỉ định, chúng tôi có thể tiến tục. Chúng tôi xét điểm cơ bản là 50 và gọi các phương thức helper, phân tích chuỗi và tính toán độ phức tạp của nó.

Nếu ngắn hơn độ dài dự kiến, chúng tôi chỉ cần xét điểm cơ bản thành 0.

Sau đó, chúng ta gọi hàm outputResult sẽ quan tâm đến ý nghĩa của các phép toán đã được tính toán. Chúng ta sẽ thấy cách nó hoạt động sau đây.

Phân tích dữ liệu nhập vào

function analyzeString ()
{   
    for (i=0; i<charPassword.length;i++)
    {
        if (charPassword[i].match(/[A-Z]/g)) {num.Upper++;}
        if (charPassword[i].match(/[0-9]/g)) {num.Numbers++;}
        if (charPassword[i].match(/(.*[!,@,#,$,%,^,&,*,?,_,~])/)) {num.Symbols++;} 
    }
     
    num.Excess = charPassword.length - minPasswordLength;
     
    if (num.Upper && num.Numbers && num.Symbols)
    {
        bonus.Combo = 25; 
    }
 
    else if ((num.Upper && num.Numbers) || (num.Upper && num.Symbols) || (num.Numbers && num.Symbols))
    {
        bonus.Combo = 15; 
    }
     
    if (strPassword.match(/^[\sa-z]+$/))
    { 
        bonus.FlatLower = -15;
    }
     
    if (strPassword.match(/^[\s0-9]+$/))
    { 
        bonus.FlatNumber = -35;
    }
}

Có thể trông hơi phức tạp nhưng tôi hứa với bạn, đó chỉ là do các biểu thức thông thường. Chúng ta hãy xem code từng phần một.

Đầu tiên, chúng ta cần tìm ra thành phần của chuỗi cần xem xét. Chúng ta cần tìm hiểu xem chuỗi có chứa chữ in hoa, số ký hiệu hay không và nếu thế thì có bao nhiêu đang hiện diện. Với suy nghĩ này, chúng tôi lặp lại qua mảng ký tự và kiểm tra từng ký tự để xem kiểu của nó. Phương thức match cho phép chúng ta khớp một chuỗi với regular expression. Nếu bạn chưa quen với regular expression, tôi khuyên bạn nên đọc bài viết tuyệt vời của Vasili tại đây.

Tiếp theo, chúng tôi đã xác định sự khác biệt giữa độ dài của chuỗi dữ liệu nhập vào và độ dài tối thiểu được chỉ định cho mật khẩu. Điều này cho chúng ta số lượng ký tự dư thừa để thử nghiệm.

Sau đó chúng tôi kiểm tra xem chuỗi có chữ hoa, số và ký hiệu không. Nếu vậy, hãy cho bonus. Chúng tôi cũng kiểm tra xem liệu nó có sự kết hợp của hai trong số các điều đó hay không và cấp bonus nhỏ hơn nếu có.

Cuối cùng, chúng tôi kiểm tra xem một chuỗi có flat hay không: liệu nó chỉ chứa các chữ cái in thường hay chỉ các con số. Chúng tôi kiểm tra điều này với một regular expression và nếu vậy, sẽ phạt mật khẩu cho thực hành này.

Tính toán độ phức tạp

function calcComplexity()
{
    score = baseScore + (num.Excess*bonus.Excess) + (num.Upper*bonus.Upper) + (num.Numbers*bonus.Numbers) + 
(num.Symbols*bonus.Symbols) + bonus.Combo + bonus.FlatLower + bonus.FlatNumber; 
}

Chỉ là một bổ sung đơn giản. Chúng tôi thêm điểm cơ bản vào sản phẩm của số lượng ký tự thừa và số nhân của nó. Tương tự cho chữ in hoa, con số và ký hiệu. Sau đó, chúng tôi sẽ thêm bonus cho các kết hợp nếu có và bổ sung phạt nếu chuỗi là flat.

Cập nhật giao diện người dùng

Bây giờ tất cả các tính toán là động cơ của chúng tôi, chúng tôi có thể cập nhật giao diện người dùng để phản ánh các thay đổi. Đây là mỗi trạng thái.

Points
Points
Points
Points

function outputResult()
{
    if ($("#inputPassword").val()== "")
    { 
        complexity.html("Enter a random value").addClass("default");
    }
    else if (charPassword.length < minPasswordLength)
    {
        complexity.html("At least " + minPasswordLength+ " characters please!").addClass("weak");
    }
    else if (score<50)
    {
        complexity.html("Weak!").addClass("weak");
    }
    else if (score>=50 && score<75)
    {
        complexity.html("Average!").addClass("strong");
    }
    else if (score>=75 && score<100)
    {
        complexity.html("Strong!").addClass("stronger");
    }
    else if (score>=100)
    {
        complexity.html("Secure!").addClass("strongest");
    }
}

Không có gì lạ ở đây nhưng chúng ta sẽ xem qua từng dòng một.

Trước tiên chúng tôi kiểm tra xem liệu dữ liệu nhập vào có trống không. Nếu vậy, hãy thay đổi text của kết quả và thêm một class mặc định để thay đổi màu nền của nó để trở lại màu xám ban đầu.

Nếu nó nhỏ ngắn độ dài tối thiểu được chỉ định, chúng tôi sẽ thay đổi văn bản tương ứng và thêm một class weak để nền của nó có màu đỏ. Tương tự nếu tổng số điểm dưới 50 nhưng thay đổi text thành weak.

Khi điểm số tăng lên, chúng tôi thay đổi text cho phù hợp và bổ sung các class cần thiết. Hãy thoải mái thay đổi điểm cơ sở cho từng xếp hạng. Tôi chỉ đưa vào các giá trị không theo khoa học cho bản demo diễn ra.

Cập nhật bảng phân tích chi tiết

Points

Với kết quả chính được cập nhật, chúng ta có thể xem xét cập nhật số liệu thống kê ngay bây giờ.

function outputResult()
{
    // Previous Code
     
    $("#details").html("Base Score :<span class=\"value\">" + baseScore  + "</span>"
                  + "<br />Length Bonus :<span class=\"value\">" + (num.Excess*bonus.Excess) + " ["+num.Excess+"x"+bonus.Excess+"]</span> "
                  + "<br />Upper case bonus :<span class=\"value\">" + (num.Upper*bonus.Upper) + " ["+num.Upper+"x"+bonus.Upper+"]</span> "
                  + "<br />Number Bonus :<span class=\"value\"> " + (num.Numbers*bonus.Numbers) + " ["+num.Numbers+"x"+bonus.Numbers+"]</span>"
                  + "<br />Symbol Bonus :<span class=\"value\"> " + (num.Symbols*bonus.Symbols) + " ["+num.Symbols+"x"+bonus.Symbols+"]</span>"
                  + "<br />Combination Bonus :<span class=\"value\"> " + bonus.Combo + "</span>"
                  + "<br />Lower case only penalty :<span class=\"value\"> " + bonus.FlatLower + "</span>"
                  + "<br />Numbers only penalty :<span class=\"value\"> " + bonus.FlatNumber + "</span>"
                  + "<br />Total Score:<span class=\"value\"> " + score  + "</span>"
}

Phần này không khó hiểu như bạn thấy. Hãy để tôi giải thích.

Thay vì chỉ cập nhật các giá trị riêng lẻ cho kết quả chi tiết, tôi đã dùng đến việc chỉ cập nhật hoàn toàn giá trị HTML của container. Tôi biết rằng điều này sẽ chậm khi một số box này bổ sung nhưng việc truy cập từng thành phần và sau đó cập nhật giá trị của nó cho một bản demo nhỏ dường như khá phản tác dụng. Vậy hãy đi cùng tôi.

Điều này giống như việc đưa HTML thông thường vào một phần tử ngoại trừ việc chúng ta đã đưa vào vài biến bên trong để cho phép các chi tiết được cập nhật tức thời. Mỗi giá trị được một class value để in đậm. Chúng tôi cũng hiển thị số lượng ký tự đặc biệt và bội số của nó để người dùng có thể đánh giá các yếu tố nào quan trọng hơn.

Một số tinh chỉnh

Tại thời điểm này, có 2 lỗi xuất hiện.

  • Nếu bạn nhập mật khẩu dài và sau đó xóa text box, màu nền sẽ không thay đổi.
  • Trong cùng một kịch bản, các chi tiết về các điểm bị thất bại không cập nhật như bình thường.

Chúng tôi sẽ giải quyết từng việc một.

Đối với lỗi đầu tiên, nguyên nhân gốc rễ là do chúng ta không xóa tất cả các class khác. Điều này sẽ không thành vấn đề nếu các class mới được thêm được ưu tiên hơn các class khác. Thật không may, điều đó không phải như vậy. Đây là một cách sửa nhanh chóng.

function outputResult()
{
    if ($("#inputPassword").val()== "")
    { complexity.html("Enter a random value").removeClass("weak strong stronger strongest").addClass("default");}
    else if (charPassword.length < minPasswordLength)
    {complexity.html("At least " + minPasswordLength+ " characters please!").removeClass("strong stronger strongest").addClass("weak");}
    else if (score<50)
    {complexity.html("Weak!").removeClass("strong stronger strongest").addClass("weak");}
    else if (score>=50 && score<75)
    {complexity.html("Average!").removeClass("stronger strongest").addClass("strong");}
    else if (score>=75 && score<100)
    {complexity.html("Strong!").removeClass("strongest").addClass("stronger");}
    else if (score>=100)
    {complexity.html("Secure!").addClass("strongest");}
 
    // Details updating code
}

Có lẽ bạn đang hỏi tại sao chúng tôi không loại bỏ từng class ở đây. Câu trả lời rất đơn giản: chúng tôi tận dụng một trong những thuộc tính chính của CSS: cascading. Nếu bạn lưu ý thứ tự khai báo của từng class trong file CSS, bạn sẽ nhận thấy rằng default (mặc định) xuất hiện lần đầu tiên và strongest (mạnh nhất) diễn ra sau cùng, có nghĩa là nếu một phần tử có class strongest thì nó sẽ ghi đè mọi sửa đổi được thực hiện bởi bất kỳ class nào bên trên nó. Vì vậy, chúng ta sẽ chỉ phải loại bỏ các class nằm bên dưới class có liên quan. Ví dụ, để một thành phần trở nên strong (mạnh), chúng ta sẽ phải loại bỏ các class stronger và strongest.

Lý do lỗi thứ hai tồn tại là do thực tế là các biến riêng lẻ không được xét lại khi có sự kiện mới xảy ra. Chúng vẫn như thế khi các sự kiện tiếp theo diễn ra. Để khắc phục lỗi này, chúng tôi tạo nhanh một hàm, hãy khởi tạo lại tất cả các biến có liên quan và bổ sung vào trình xử lý sự kiện checkVal để nó được gọi mỗi khi text của input box được cập nhật.

function init()
{
    strPassword= $("#inputPassword").val();
    charPassword = strPassword.split("");
         
    num.Excess = 0;
    num.Upper = 0;
    num.Numbers = 0;
    num.Symbols = 0;
    bonus.Combo = 0; 
    bonus.FlatLower = 0;
    bonus.FlatNumber = 0;
    baseScore = 0;
    score =0;
}
function checkVal()
{
    init();
     
    // Other code
}

Những hạn chế

Limitation with the current implementation

Nếu bạn đã thử bản demo một chút, bạn sẽ nhận thấy Pa$$W0rd$ nổi bật như một mật khẩu an toàn trong khi thực tế nó sẽ bị phá vỡ khá nhanh. Điều này là do sự đơn giản trong thuật toán của chúng tôi ở đây. Chúng tôi không kiểm tra việc thay thế ký tự. Hoặc các mật khẩu hoặc pattern phổ biến cho vấn đề đó. Thực hiện những việc như vậy sẽ làm tăng độ khó của hướng dẫn này trong khi giảm khả năng tiếp cận của nó, cả hai điều tôi không muốn cho bài viết cụ thể này.

Điều này được dự định như là một test cơ bản cho sức mạnh mật khẩu. Nếu bạn cần tăng cường cho nó, có lẽ bạn có thể bổ sung một vài regular expression để test các pattern và lặp lại ký tự và sau đó điều chỉnh kết quả cho phù hợp.

Việc nhìn vào dữ liệu nhập vào so với một từ điển thực sự nằm ngoài phạm vi của bài viết này và sẽ yêu cầu máy khách tải về một từ điển khổng lồ hoặc kết nối nó với hệ thống phía máy chủ để thực hiện điều đó. Lần nữa tôi thực sự muốn tránh cả hai.

Tổng kết

Và bạn đã hoàn thành: cách để thêm chức năng thân thiện với người dùng, khả năng cho người dùng biết độ mạnh của mật khẩu anh ta vừa nhập, cho các dự án của bạn. Hy vọng rằng bạn đã tìm thấy hướng dẫn này thú vị và hữu ích cho bạn. Hãy thoải mái sử dụng lại code này ở chỗ khác trong các dự án của bạn và hãy cho biết nếu bạn gặp khó khăn.

Có câu hỏi gì không? Có gì hay để nói không? Có lời phê bình nào không? Hãy nhấn vào phần bình luận và cho tôi một bình luận. Code vui vẻ!

Be the first to comment

Leave a comment

Your email address will not be published.


*