문제

저는 비밀번호가 얼마나 강력한지 정확하게 알려줄 수 있는 효과적인 알고리즘을 찾고 있었습니다.

웹사이트마다 비밀번호 강도 등급이 다르기 때문에 여러 웹사이트에서 서로 다른 알고리즘을 사용한다는 사실을 발견했습니다.

도움이 되었습니까?

해결책

이것은 PHP/MySQL에서 암호로 작업하기위한 모범 사례의 일반적인 뇌 덤프로 성장했습니다. 여기에 제시된 아이디어는 일반적으로 내 자신이 아니라 내가 찾은 것 중 최고입니다.


사용자 정보와 관련된 모든 작업에 SSL을 사용하고 있는지 확인하십시오. 이러한 양식과 관련된 모든 페이지는 HTTPS를 통해 호출되는지 확인하고 그렇지 않으면 작업을 거부해야합니다.

허용되지 않은 로그인 수를 제한하여 대부분의 공격을 제거 할 수 있습니다.

비교적 약한 비밀번호를 허용하지만 사용자 당 실패한 로그인 수를 저장하고 초과 할 경우 이메일로 보안 문자 또는 비밀번호 확인이 필요합니다. 최대 장애를 5로 설정했습니다.

사용자에게 로그인 실패를 제시하면 공격자에게 정보를 제공하지 않도록 신중하게 생각해야합니다. 존재하지 않는 사용자로 인한 로그인이 실패하면 비밀번호가 나쁘기 때문에 로그인이 실패한 것과 동일한 메시지를 반환해야합니다. 다른 메시지를 제공하면 공격자가 유효한 사용자 로그인을 결정할 수 있습니다.

또한 유효한 암호가있는 로그인이 너무 많고 로그인이 너무 많고 암호가 잘못된 실패로 인해 정확히 동일한 메시지를 반환해야합니다. 다른 메시지를 제공하면 공격자가 유효한 사용자 비밀번호를 결정할 수 있습니다. 암호를 재설정 할 때 상당한 수의 사용자는 단순히 비밀번호를 다시 한 번 돌려줍니다.

불행히도 IP 주소 당 허용되는 로그인 수를 제한하는 것은 실용적이지 않습니다. AOL 및 대부분의 회사와 같은 일부 제공 업체는 웹 요청을 프록시합니다. 이 한도를 부과하면 이러한 사용자가 효과적으로 제거됩니다.


JavaScript로 클라이언트에게 사전을 보내거나 필드 변경 당 AJAX 요청을 보내야하므로 제출하기 전에 사전 단어를 확인하는 것을 발견했습니다. 나는 이것을 잠시 동안 해냈고 괜찮 았지만 생성 된 트래픽이 마음에 들지 않았습니다.

본질적으로 약한 암호를 확인하는 것은 사전 단어를 뺀 것입니다.

제출 후 사전 단어와 비밀번호가 포함 된 사용자 이름을 확인하고 서버 측면을 확인합니다. 아주 좋은 사전은 쉽게 다운로드 할 수 있으며 테스트는 간단합니다. 여기서 Gotcha 중 하나는 사전 단어를 테스트하려면 데이터베이스에 대해 쿼리를 보내야한다는 것입니다. 여기에는 암호가 포함되어 있습니다. 내가 이것을 돌아 다니는 방식은 간단한 암호화와 엔드 배치 소금으로 사전을 손으로 암호화 한 다음 암호화 된 암호를 테스트하는 것이 었습니다. 이상적이지는 않지만 일반 텍스트보다 낫고 물리적 기계와 서브넷의 사람들을위한 와이어 만 있습니다.

비밀번호에 만족하면 PHP를 먼저 암호화 한 다음 저장했습니다. 다음 비밀번호 암호화 기능은 내 생각이 아니지만 여러 가지 문제를 해결합니다. PHP 내에서 암호화하면 공유 서버의 사람들이 암호화되지 않은 암호를 가로 채지 못하게합니다. 변경하지 않는 사용자 당 추가 (이것은 내 사이트의 사용자 이름이므로 이메일을 사용합니다) 해시를 추가하면 (소금이 사이트마다 변경되는 짧은 상수 문자열이라) 공격에 대한 저항이 증가합니다. 소금은 비밀번호 내에 있고 암호의 길이가 될 수 있으므로 무지개 테이블로 이것을 공격하는 것은 거의 불가능 해집니다. 또는 그것은 또한 사람들이 이메일을 변경할 수 없으며 모든 사람의 암호를 무효화하지 않으면 소금을 변경할 수 없다는 것을 의미합니다.

편집 : 이제 사용하는 것이 좋습니다 phpass 내 롤 대신 여기에서 자신의 기능 대신에 사용자 로그인을 모두 잊고 사용합니다. 개방형 대신에.

function password_crypt($email,$toHash) {
  $password = str_split($toHash,(strlen($toHash)/2)+1);
  return hash('sha256', $email.$password[0].SALT.$password[1]); 
}

내 jQueryish 클라이언트 측 비밀번호 미터. 대상은 div 여야합니다. 너비는 0과 100 사이에서 변경되며 스크립트에 표시된 클래스에 따라 배경색이 변경됩니다. 다시 한 번 내가 찾은 다른 것들에서 도난당했습니다.

$.updatePasswordMeter = function(password,username,target) {
$.updatePasswordMeter._checkRepetition = function(pLen,str) {
res = ""
for ( i=0; i<str.length ; i++ ) {
    repeated=true;
    for (j=0;j < pLen && (j+i+pLen) < str.length;j++)
        repeated=repeated && (str.charAt(j+i)==str.charAt(j+i+pLen));
    if (j<pLen) repeated=false;
    if (repeated) {
        i+=pLen-1;
        repeated=false;
    }
    else {
        res+=str.charAt(i);
    };
};
return res;
};
var score = 0;
var r_class = 'weak-password';
//password < 4
if (password.length < 4 || password.toLowerCase()==username.toLowerCase()) { 
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
  return true;
} 
//password length
score += password.length * 4;
score += ( $.updatePasswordMeter._checkRepetition(1,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(2,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(3,password).length - password.length ) * 1;
score += ( $.updatePasswordMeter._checkRepetition(4,password).length - password.length ) * 1;
//password has 3 numbers
if (password.match(/(.*[0-9].*[0-9].*[0-9])/))  score += 5; 

//password has 2 symbols
if (password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5; 

//password has Upper and Lower chars
if (password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/))  score += 10; 

//password has number and chars
if (password.match(/([a-zA-Z])/) && password.match(/([0-9])/))  score += 15; 
//
//password has number and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([0-9])/))  score += 15; 

//password has char and symbol
if (password.match(/([!,@,#,$,%,^,&,*,?,_,~])/) && password.match(/([a-zA-Z])/))  score += 15; 

//password is just a nubers or chars
if (password.match(/^\w+$/) || password.match(/^\d+$/) )  score -= 10; 

//verifing 0 < score < 100
score = score * 2;
if ( score < 0 )  score = 0;
if ( score > 100 )  score = 100;
if (score > 25 ) r_class = 'okay-password';
if (score > 50  ) r_class = 'good-password';
if (score > 75 ) r_class = 'strong-password';
target.width(score + '%').removeClass("weak-password okay-password good-password strong-password"
).addClass(r_class);
return true;
};

다른 팁

기본적으로 주요 유형의 공격을 방지하고 싶습니다.

  • 사전 공격
  • 무자비한 힘 공격

첫 번째를 방지하기 위해 일반적인 단어가 약한 암호를 고려하려고합니다. 두 번째를 방지하기 위해 합리적인 길이의 암호를 장려하고 (8 개 이상의 문자는 일반적입니다) 합리적으로 큰 문자 세트 (문자, 숫자 및 특수 문자 포함)를 사용하려고합니다. 소문자와 대문자 문자가 다른 것으로 간주하면 캐릭터 세트가 크게 증가합니다. 그러나 이는 일부 사용자 커뮤니티에 유용성 문제가 발생하므로 해당 고려 사항의 균형을 맞춰야합니다.

빠른 Google 검색은 무차별 인력 공격 (복잡한 비밀번호)을 설명하지만 사전 공격의 경우가 아닌 솔루션을 설정했습니다. PHP 비밀번호 강도 미터 이 강도 검사기 목록 검사 서버 측면을 실행하므로 사전을 확인하도록 확장 할 수 있습니다.

편집하다:

그건 그렇고 ... 사용자 당 로그인 시도 수를 제한해야합니다. 이것은 두 가지 유형의 공격이 가능성이 떨어집니다. 효과적이지만 사용자에게 친숙하지 않은 것은 X가 잘못된 시도 후 계정을 잠그고 비밀번호 재설정이 필요합니다. 보다 사용자 친화적이지만 더 많은 노력은 로그인 시도 사이에 시간을 스로틀하는 것입니다. 당신은 또한 필요할 수 있습니다 CARTCHA 처음 몇 번의 로그인 시도 후 (이는 너무 많은 편집 후 또는 매우 새로운 사용자에게 스택 오버플로가 필요한 것).

기본적으로 정규식을 사용하여 암호의 길이와 복잡성을 검증하려고합니다.

JavaScript를 사용 하여이 작업을 수행하는 좋은 예는 여기에서 찾을 수 있습니다.

http://marketingtechblog.com/programming/javaScript-password-strength/

Daren Schwenke가 지적했듯이, 보안 작업을 직접 수행하고 이를 사용자 손에 맡기지 않는 것이 좋습니다..

하지만 사용자에게 비밀번호가 얼마나 강력한지에 대한 몇 가지 힌트를 제공하는 것이 좋습니다. 비밀번호를 얻는 가장 좋은 방법은 여전히 ​​소셜 생성입니다..

따라서 예의 지표로 사용자 비밀번호 강도를 실시간으로 확인하는 작은 클라이언트 측 스크립트를 해킹할 수 있습니다.아무것도 차단하지 않지만 녹색으로 바뀌면 좋은 따뜻한 느낌을 줍니다 :-)

기본적으로 확인해야 할 것은 상식입니다.비밀번호에 문자, 숫자 및 알파벳이 아닌 문자가 합리적인 양으로 포함되어 있는지 확인하십시오.

자신만의 알고리즘을 매우 쉽게 해킹할 수 있습니다.그냥 10/10 표시를 하세요:

  • 0은 길이가 0인 비밀번호입니다.
  • 비밀번호의 8자마다 +2(안전한 길이는 15자라고 가정함)
  • 문자 사용 시 +1, 문자 2개 사용 시 +2
  • 숫자 사용 시 +1, 숫자 2개 사용 시 +2
  • 알파벳이 아닌 문자를 사용하면 +1, 2이면 +2.

신과 같은 비밀번호(대문자가 있는지, 특수 문자가 어디에 있는지 등)를 확인할 필요가 없습니다. 사용자는 은행/군인/비밀 기관/월간 파이썬 영화 산업에 종사하지 않습니다. 그렇죠?

자바스크립트 기술이 없어도 한 시간 안에 코딩할 수 있습니다.

그리고 어쨌든 비밀번호를 확인하고 모든 보안 코드를 서버 측으로 이동하세요.인증을 위임할 수 있는 경우(예:오픈ID)가 더 좋습니다.

비밀번호의 강도를 확인하기위한 특정 알고리즘을 생각할 수 없습니다. 우리가하는 일은 몇 가지 기준을 정의하고 비밀번호가 기준을 존중할 때 1 점을 추가합니다. 암호가 임계 값에 도달하면 비밀번호가 강합니다. 그렇지 않으면 약합니다.

다른 throeshold가있는 경우 많은 다른 수준의 Strengh를 정의하거나 특정 기준에 대해 다른 값을 정의 할 수 있습니다. 예를 들어, 비밀번호에 5 문자가 있으면 1을 추가하지만 10을 얻은 경우 2를 추가합니다.

다음은 확인할 기준 목록입니다

길이 (8 ~ 12는 괜찮고, 더 나은 것이 더 좋습니다) 소문자가 포함 된 대문자 문자가 포함되어 있습니다. 대문자 문자는 첫 번째 문자가 아닙니다. 숫자가 포함되어있는 기호가 포함되어 있습니다. 마지막 문자는 인간과 같은 사람이 아닙니다 (예 :. 또는!)는 사전 단어처럼 보이지 않습니다. 일부 현명한 비밀번호 균열에는 단어 라이브러리와 문자 대체물이 포함되어 있습니다 (라이브러리 -> l1br@ry)

도움이되기를 바랍니다.

롤을하지 마십시오!

암호화 전문가들은 자신의 암호화를 방해합니다 분명 해야하는 이유로.

같은 이유로, 암호의 강도를 측정하는 문제에 대해 자신의 솔루션을 굴려서는 안됩니다. 암호화 문제입니다.

이 목적을 위해 엄청난 정규 표현을 작성하는 추악한 사업에 들어 가지 마십시오. 비밀번호의 전반적인 강점에 영향을 미치는 몇 가지 요인을 설명하지 못할 것입니다.

어려운 문제입니다

비밀번호의 강도를 측정하는 문제에 내재 된 어려움이 있습니다. 이 주제에 대한 연구가 많을수록 이것이 "단방향"문제라는 것을 더 많이 알게됩니다. 즉, 비밀번호를 크래킹하는 "난이도"(계산 비용)를 측정 할 수 없습니다. 효율적으로. 오히려 복잡성 요구 사항을 제공하고 비밀번호를 충족시키는 능력을 측정하는 것이 더 효율적입니다.

우리가 문제를 논리적으로 고려할 때, "크랙 가능성 지수"는 소리만큼 편리하지 않습니다. 계산을 주도하는 많은 요소가 있으며, 대부분은 크래킹 프로세스에 전념하는 계산 자원과 관련이 있으며, 비현실적입니다.

구덩이를 상상해보십시오 존 리퍼 존 해당 비밀번호에 대한 (또는 유사한 도구); 괜찮은 비밀번호를 깨뜨리는 데 며칠이 걸릴 수 있으며, 좋은 비밀번호를 끊는 데 몇 달, 태양이 탁월한 비밀번호를 끊을 때까지 며칠이 걸릴 수 있습니다. 이것은 암호 강도를 측정하는 실용적인 수단이 아닙니다.

다른 방향에서 문제에 접근하는 것은 훨씬 더 관리하기 쉽습니다. 복잡성 요구 사항을 제공하면 암호의 상대 강도를 매우 빠르게 판단 할 수 있습니다. 분명히, 공급 된 복잡성 요구 사항은 시간이 지남에 따라 진화해야하지만, 이러한 방식으로 문제에 접근하면 설명 할 변수가 훨씬 적습니다.

실행 가능한 솔루션

OpenWall에서 사용할 수있는 독립형 유틸리티가 있습니다 passwdqc (아마도 서서 비밀번호 품질 검사기). OpenWall 개발자 인 Solar Designer는 선의의 암호화 전문가 (그의 작품은 스스로 말하기) 인 것으로 보이며, 그러한 도구를 작성할 자격이 있습니다.

내 특별한 유스 케이스의 경우, 이것은 웹의 어두운 구석에 거주하는 잘못된 JavaScript 스 니펫을 사용하는 것보다 훨씬 매력적인 솔루션입니다.

특정 요구에 대한 매개 변수를 설정하는 것이 가장 어려운 부분입니다. 구현이 쉬운 부분입니다.

실용적인 예

PHP에서 간단한 구현을 제공하여 점프 시작을 제공합니다. 표준 면책 조항이 적용됩니다.

이 예제에서는 전체 암호 목록을 PHP 스크립트에 공급하고 있다고 가정합니다. 실제 비밀번호 (예 : 비밀번호 관리자에서 버려진 것) 로이 작업을 수행하는 경우 비밀번호 처리와 관련하여 극도로주의를 기울여야합니다. 암호화되지 않은 암호 덤프를 작성하여 디스크를 디스크로 작성하면 암호의 보안이 위태로워집니다!

passwords.csv:

"Title","Password"
"My Test Password","password123"
"Your Test Password","123456!!!"
"A strong password","NFYbCoHC5S7dngitqCD53tvQkAu3dais"

password-check.php:

<?php

//A few handy examples from other users:
//http://php.net/manual/en/function.str-getcsv.php#117692

$csv = array_map('str_getcsv', file('passwords.csv'), [',']);

array_walk($csv, function(&$a) use ($csv) {
    $a = array_combine($csv[0], $a);
});

//Remove column header.

array_shift($csv);

//Define report column headers.

$results[] = [
    'Title',
    'Result',
    'Exit Code',
];

$i = 1;

foreach ($csv as $p) {
    $row['title'] = $p['Title'];

    //If the value contains a space, it's considered a passphrase.

    $isPassphrase = stristr($p['Password'], ' ') !== false ? true : false;

    $cmd = 'echo ' . escapeshellarg($p['Password']) . ' | pwqcheck -1 min=32,24,22,20,16 max=128';

    if ($isPassphrase) {
        $cmd .= ' passphrase=3';
    }
    else {
        $cmd .= ' passphrase=0';
    }

    $output = null;
    $exitCode = null;

    $stdOut = exec($cmd, $output, $exitCode);

    //Exit code 0 represents an unacceptable password (not an error).
    //Exit code 1 represents an acceptable password (it meets the criteria).

    if ($exitCode === 0 || $exitCode === 1) {
        $row['result'] = trim($stdOut);
        $row['exitCode'] = $exitCode;
    }
    else {
        $row['result'] = 'An error occurred while calling pwqcheck';
        $row['exitCode'] = null;
    }

    $results[$i] = $row;

    $i++;
}

$reportFile = 'report.csv';

$fp = @fopen($reportFile, 'w');

if ($fp !== false) {
    foreach ($results as $p) {
        fputcsv($fp, $p);
    }

    fclose($fp);
}
else {
    die($reportFile . ' could not be opened for writing (destination is not writable or file is in use)');
}

exit;

결과적인 report.csv:

Title,Result,"Exit Code"
"My Test Password","Bad passphrase (too short)",1
"Your Test Password","Bad passphrase (too short)",1
"A strong password",OK,0

마무리

웹에서 더 철저한 솔루션을 찾지 못했습니다. 말할 것도없이, 나는 다른 권장 사항을 환영합니다.

분명히,이 접근법은 특정 사용 사례 (예 : "암호 강도 미터"구현 된 "클라이언트 측")에게는 이상적이지 않습니다. 그럼에도 불구하고 위에서 설명한 접근 방식을 사용하여 패스/실패 응답을 반환하는 서버 측 리소스를 Ajax 호출하는 것은 사소한 일이지만 이러한 접근 방식은 남용 가능성 (예 : DOS 공격)을 가정해야합니다. 클라이언트와 서버 간의 보안 및 보수되지 않은 비밀번호 전송과 관련된 위험에 대한 수용.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top