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:
parent
ae3edcc34d
commit
497b08df44
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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">
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,10 @@
|
|||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.row-top-padding {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.box-footer {
|
.box-footer {
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user