1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-26 12:25:20 +01:00

[SG-458] Master Password security checks (browser) (#4502)

* work: add base logic for password lookup

* work: added browser support for hibp

* work: SG-558 combine weak + leak warning

* fix: language stuff

* fix: form values are neater tho :(

* fix: no cast
This commit is contained in:
Brandon Maharaj 2023-01-24 16:04:01 -05:00 committed by GitHub
parent ae3edcc34d
commit 497b08df44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 101 additions and 11 deletions

View File

@ -2046,5 +2046,35 @@
}, },
"rememberEmail": { "rememberEmail": {
"message": "Remember email" "message": "Remember email"
},
"exposedMasterPassword": {
"message": "Exposed Master Password"
},
"exposedMasterPasswordDesc": {
"message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?"
},
"weakAndExposedMasterPassword": {
"message": "Weak and Exposed Master Password"
},
"weakAndBreachedMasterPasswordDesc": {
"message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?"
},
"checkForBreaches": {
"message": "Check known data breaches for this password"
},
"important": {
"message": "Important:"
},
"masterPasswordHint": {
"message": "Your master password cannot be recovered if you forget it!"
},
"characterMinimum": {
"message": "$LENGTH$ character minimum",
"placeholders": {
"length": {
"content": "$1",
"example": "14"
}
}
} }
} }

View File

@ -66,7 +66,8 @@
</div> </div>
</div> </div>
<div id="masterPasswordHelp" class="box-footer"> <div id="masterPasswordHelp" class="box-footer">
{{ "masterPassDesc" | i18n }} <b>{{ "important" | i18n }}</b> {{ "masterPasswordHint" | i18n }}
{{ characterMinimumMessage }}
</div> </div>
</div> </div>
<div class="box"> <div class="box">
@ -107,6 +108,17 @@
<div id="hintHelp" class="box-footer"> <div id="hintHelp" class="box-footer">
{{ "masterPassHintDesc" | i18n }} {{ "masterPassHintDesc" | i18n }}
</div> </div>
<div class="box-content row-top-padding">
<div
class="box-content-row box-content-row-checkbox box-content-row-checkbox-left box-content-row-word-break"
appBoxRow
>
<input type="checkbox" id="checkForBreaches" formControlName="checkForBreaches" />
<label for="checkForBreaches">
{{ "checkForBreaches" | i18n }}
</label>
</div>
</div>
</div> </div>
<div [hidden]="!showCaptcha()"><iframe id="hcaptcha_iframe" height="80"></iframe></div> <div [hidden]="!showCaptcha()"><iframe id="hcaptcha_iframe" height="80"></iframe></div>
<div class="box last" *ngIf="showTerms"> <div class="box last" *ngIf="showTerms">

View File

@ -4,6 +4,7 @@ import { Router } from "@angular/router";
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
@ -34,7 +35,8 @@ export class RegisterComponent extends BaseRegisterComponent {
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
passwordGenerationService: PasswordGenerationService, passwordGenerationService: PasswordGenerationService,
environmentService: EnvironmentService, environmentService: EnvironmentService,
logService: LogService logService: LogService,
auditService: AuditService
) { ) {
super( super(
formValidationErrorService, formValidationErrorService,
@ -48,7 +50,8 @@ export class RegisterComponent extends BaseRegisterComponent {
platformUtilsService, platformUtilsService,
passwordGenerationService, passwordGenerationService,
environmentService, environmentService,
logService logService,
auditService
); );
} }
} }

View File

@ -83,6 +83,10 @@
padding-bottom: 10px; padding-bottom: 10px;
margin: 5px; margin: 5px;
} }
&.row-top-padding {
padding-top: 10px;
}
} }
.box-footer { .box-footer {

View File

@ -4,6 +4,7 @@ import { Router } from "@angular/router";
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service"; import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
@ -36,7 +37,8 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit,
environmentService: EnvironmentService, environmentService: EnvironmentService,
private broadcasterService: BroadcasterService, private broadcasterService: BroadcasterService,
private ngZone: NgZone, private ngZone: NgZone,
logService: LogService logService: LogService,
auditService: AuditService
) { ) {
super( super(
formValidationErrorService, formValidationErrorService,
@ -50,7 +52,8 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit,
platformUtilsService, platformUtilsService,
passwordGenerationService, passwordGenerationService,
environmentService, environmentService,
logService logService,
auditService
); );
} }

View File

@ -4,6 +4,7 @@ import { Router } from "@angular/router";
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
@ -41,7 +42,8 @@ export class RegisterFormComponent extends BaseRegisterComponent {
passwordGenerationService: PasswordGenerationService, passwordGenerationService: PasswordGenerationService,
private policyService: PolicyService, private policyService: PolicyService,
environmentService: EnvironmentService, environmentService: EnvironmentService,
logService: LogService logService: LogService,
auditService: AuditService
) { ) {
super( super(
formValidationErrorService, formValidationErrorService,
@ -55,7 +57,8 @@ export class RegisterFormComponent extends BaseRegisterComponent {
platformUtilsService, platformUtilsService,
passwordGenerationService, passwordGenerationService,
environmentService, environmentService,
logService logService,
auditService
); );
} }

View File

@ -3,6 +3,7 @@ import { AbstractControl, UntypedFormBuilder, ValidatorFn, Validators } from "@a
import { Router } from "@angular/router"; import { Router } from "@angular/router";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { AuditService } from "@bitwarden/common/abstractions/audit.service";
import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service";
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
@ -38,6 +39,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
showTerms = true; showTerms = true;
showErrorSummary = false; showErrorSummary = false;
passwordStrengthResult: any; passwordStrengthResult: any;
characterMinimumMessage: string;
minimumLength = 8;
color: string; color: string;
text: string; text: string;
@ -45,8 +48,8 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
{ {
email: ["", [Validators.required, Validators.email]], email: ["", [Validators.required, Validators.email]],
name: [""], name: [""],
masterPassword: ["", [Validators.required, Validators.minLength(8)]], masterPassword: ["", [Validators.required, Validators.minLength(this.minimumLength)]],
confirmMasterPassword: ["", [Validators.required, Validators.minLength(8)]], confirmMasterPassword: ["", [Validators.required, Validators.minLength(this.minimumLength)]],
hint: [ hint: [
null, null,
[ [
@ -56,6 +59,7 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
), ),
], ],
], ],
checkForBreaches: [false],
acceptPolicies: [false, [this.acceptPoliciesValidation()]], acceptPolicies: [false, [this.acceptPoliciesValidation()]],
}, },
{ {
@ -85,10 +89,12 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
platformUtilsService: PlatformUtilsService, platformUtilsService: PlatformUtilsService,
protected passwordGenerationService: PasswordGenerationService, protected passwordGenerationService: PasswordGenerationService,
environmentService: EnvironmentService, environmentService: EnvironmentService,
protected logService: LogService protected logService: LogService,
protected auditService: AuditService
) { ) {
super(environmentService, i18nService, platformUtilsService); super(environmentService, i18nService, platformUtilsService);
this.showTerms = !platformUtilsService.isSelfHost(); this.showTerms = !platformUtilsService.isSelfHost();
this.characterMinimumMessage = this.i18nService.t("characterMinimum", this.minimumLength);
} }
async ngOnInit() { async ngOnInit() {
@ -212,7 +218,24 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
return { isValid: false }; return { isValid: false };
} }
if (this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3) { const passwordWeak =
this.passwordStrengthResult != null && this.passwordStrengthResult.score < 3;
const passwordLeak =
this.formGroup.controls.checkForBreaches.value &&
(await this.auditService.passwordLeaked(this.formGroup.controls.masterPassword.value)) > 0;
if (passwordWeak && passwordLeak) {
const result = await this.platformUtilsService.showDialog(
this.i18nService.t("weakAndBreachedMasterPasswordDesc"),
this.i18nService.t("weakAndExposedMasterPassword"),
this.i18nService.t("yes"),
this.i18nService.t("no"),
"warning"
);
if (!result) {
return { isValid: false };
}
} else if (passwordWeak) {
const result = await this.platformUtilsService.showDialog( const result = await this.platformUtilsService.showDialog(
this.i18nService.t("weakMasterPasswordDesc"), this.i18nService.t("weakMasterPasswordDesc"),
this.i18nService.t("weakMasterPassword"), this.i18nService.t("weakMasterPassword"),
@ -223,7 +246,19 @@ export class RegisterComponent extends CaptchaProtectedComponent implements OnIn
if (!result) { if (!result) {
return { isValid: false }; return { isValid: false };
} }
} else if (passwordLeak) {
const result = await this.platformUtilsService.showDialog(
this.i18nService.t("exposedMasterPasswordDesc"),
this.i18nService.t("exposedMasterPassword"),
this.i18nService.t("yes"),
this.i18nService.t("no"),
"warning"
);
if (!result) {
return { isValid: false };
} }
}
return { isValid: true }; return { isValid: true };
} }