mirror of
https://github.com/Bubka/2FAuth.git
synced 2025-06-25 22:41:57 +02:00
Remove the ability to set a plain text secret
This commit is contained in:
parent
6f56c84928
commit
b6e4cf50a4
@ -28,4 +28,15 @@ class Helpers
|
|||||||
// We use the regex for semver detection (see https://semver.org/)
|
// We use the regex for semver detection (see https://semver.org/)
|
||||||
return preg_match('/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/', $release, $version) ? $version[0] : false;
|
return preg_match('/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/', $release, $version) ? $version[0] : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a string to comply with Base32 format
|
||||||
|
*
|
||||||
|
* @param string $str
|
||||||
|
* @return string The filename
|
||||||
|
*/
|
||||||
|
public static function PadToBase32Format(?string $str): string
|
||||||
|
{
|
||||||
|
return blank($str) ? '' : strtoupper(str_pad($str, (int) ceil(strlen($str) / 8) * 8, '='));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ class TwoFAccount extends Model implements Sortable
|
|||||||
public function setSecretAttribute($value)
|
public function setSecretAttribute($value)
|
||||||
{
|
{
|
||||||
// Encrypt when needed
|
// Encrypt when needed
|
||||||
$this->attributes['secret'] = $this->encryptOrReturn($value);
|
$this->attributes['secret'] = $this->encryptOrReturn(Helpers::PadToBase32Format($value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Rules;
|
namespace App\Rules;
|
||||||
|
|
||||||
|
use App\Helpers\Helpers;
|
||||||
use Illuminate\Contracts\Validation\Rule;
|
use Illuminate\Contracts\Validation\Rule;
|
||||||
use ParagonIE\ConstantTime\Base32;
|
use ParagonIE\ConstantTime\Base32;
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ class IsBase32Encoded implements Rule
|
|||||||
public function passes($attribute, $value)
|
public function passes($attribute, $value)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$secret = Base32::decodeUpper($value);
|
$secret = Base32::decodeUpper(Helpers::PadToBase32Format($value));
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
13
package-lock.json
generated
13
package-lock.json
generated
@ -9,11 +9,10 @@
|
|||||||
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
"@fortawesome/free-brands-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||||
"@fortawesome/vue-fontawesome": "^2.0.6",
|
"@fortawesome/vue-fontawesome": "^2.0.6",
|
||||||
"axios": "^1.0.0",
|
"axios": "^1.1.0",
|
||||||
"bulma": "^0.9.3",
|
"bulma": "^0.9.3",
|
||||||
"bulma-checkradio": "^2.1.2",
|
"bulma-checkradio": "^2.1.2",
|
||||||
"bulma-switch": "^2.0.0",
|
"bulma-switch": "^2.0.0",
|
||||||
"hi-base32": "^0.5.1",
|
|
||||||
"object-equals": "^0.3.0",
|
"object-equals": "^0.3.0",
|
||||||
"v-clipboard": "^2.2.3",
|
"v-clipboard": "^2.2.3",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
@ -5116,11 +5115,6 @@
|
|||||||
"he": "bin/he"
|
"he": "bin/he"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hi-base32": {
|
|
||||||
"version": "0.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz",
|
|
||||||
"integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA=="
|
|
||||||
},
|
|
||||||
"node_modules/hmac-drbg": {
|
"node_modules/hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
@ -13647,11 +13641,6 @@
|
|||||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"hi-base32": {
|
|
||||||
"version": "0.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz",
|
|
||||||
"integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA=="
|
|
||||||
},
|
|
||||||
"hmac-drbg": {
|
"hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
"bulma": "^0.9.3",
|
"bulma": "^0.9.3",
|
||||||
"bulma-checkradio": "^2.1.2",
|
"bulma-checkradio": "^2.1.2",
|
||||||
"bulma-switch": "^2.0.0",
|
"bulma-switch": "^2.0.0",
|
||||||
"hi-base32": "^0.5.1",
|
|
||||||
"object-equals": "^0.3.0",
|
"object-equals": "^0.3.0",
|
||||||
"v-clipboard": "^2.2.3",
|
"v-clipboard": "^2.2.3",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.6.14",
|
||||||
|
@ -101,14 +101,7 @@
|
|||||||
<div v-if="form.otp_type">
|
<div v-if="form.otp_type">
|
||||||
<!-- secret -->
|
<!-- secret -->
|
||||||
<label :for="this.inputId('text','secret')" class="label" v-html="$t('twofaccounts.forms.secret.label')"></label>
|
<label :for="this.inputId('text','secret')" class="label" v-html="$t('twofaccounts.forms.secret.label')"></label>
|
||||||
<div class="field has-addons">
|
<div class="field">
|
||||||
<p class="control">
|
|
||||||
<span class="select">
|
|
||||||
<select @change="form.secret=''" v-model="secretIsBase32Encoded">
|
|
||||||
<option v-for="(format) in secretFormats" :key="format.value" :value="format.value">{{ format.text }}</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p class="control is-expanded">
|
<p class="control is-expanded">
|
||||||
<input :id="this.inputId('text','secret')" class="input" type="text" v-model="form.secret">
|
<input :id="this.inputId('text','secret')" class="input" type="text" v-model="form.secret">
|
||||||
</p>
|
</p>
|
||||||
@ -146,7 +139,7 @@
|
|||||||
</form>
|
</form>
|
||||||
<!-- modal -->
|
<!-- modal -->
|
||||||
<modal v-model="ShowTwofaccountInModal">
|
<modal v-model="ShowTwofaccountInModal">
|
||||||
<otp-displayer ref="AdvancedFormOtpDisplayer" v-bind="otpdisplayerData" @increment-hotp="incrementHotp" @validation-error="mapDisplayerErrors">
|
<otp-displayer ref="AdvancedFormOtpDisplayer" v-bind="form.data()" @increment-hotp="incrementHotp" @validation-error="mapDisplayerErrors">
|
||||||
</otp-displayer>
|
</otp-displayer>
|
||||||
</modal>
|
</modal>
|
||||||
</form-wrapper>
|
</form-wrapper>
|
||||||
@ -198,7 +191,6 @@
|
|||||||
import Modal from '../../components/Modal'
|
import Modal from '../../components/Modal'
|
||||||
import Form from './../../components/Form'
|
import Form from './../../components/Form'
|
||||||
import OtpDisplayer from '../../components/OtpDisplayer'
|
import OtpDisplayer from '../../components/OtpDisplayer'
|
||||||
import Base32 from "hi-base32"
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -209,7 +201,6 @@
|
|||||||
showAlternatives : false,
|
showAlternatives : false,
|
||||||
tempIcon: '',
|
tempIcon: '',
|
||||||
uri: '',
|
uri: '',
|
||||||
secretIsBase32Encoded: 0,
|
|
||||||
form: new Form({
|
form: new Form({
|
||||||
service: '',
|
service: '',
|
||||||
account: '',
|
account: '',
|
||||||
@ -235,10 +226,6 @@
|
|||||||
{ text: 9, value: 9 },
|
{ text: 9, value: 9 },
|
||||||
{ text: 10, value: 10 },
|
{ text: 10, value: 10 },
|
||||||
],
|
],
|
||||||
secretFormats: [
|
|
||||||
{ text: this.$t('twofaccounts.forms.plain_text'), value: 0 },
|
|
||||||
{ text: 'Base32', value: 1 }
|
|
||||||
],
|
|
||||||
algorithms: [
|
algorithms: [
|
||||||
{ text: 'sha1', value: 'sha1' },
|
{ text: 'sha1', value: 'sha1' },
|
||||||
{ text: 'sha256', value: 'sha256' },
|
{ text: 'sha256', value: 'sha256' },
|
||||||
@ -260,17 +247,6 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
otpdisplayerData: function() {
|
|
||||||
let o = this.form.data()
|
|
||||||
o.secret = this.secretIsBase32Encoded
|
|
||||||
? o.secret
|
|
||||||
: Base32.encode(o.secret).toString();
|
|
||||||
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
if( this.$route.params.decodedUri ) {
|
if( this.$route.params.decodedUri ) {
|
||||||
this.uri = this.$route.params.decodedUri
|
this.uri = this.$route.params.decodedUri
|
||||||
@ -279,7 +255,6 @@
|
|||||||
this.axios.post('/api/v1/twofaccounts/preview', { uri: this.uri }).then(response => {
|
this.axios.post('/api/v1/twofaccounts/preview', { uri: this.uri }).then(response => {
|
||||||
|
|
||||||
this.form.fill(response.data)
|
this.form.fill(response.data)
|
||||||
this.secretIsBase32Encoded = 1
|
|
||||||
this.tempIcon = response.data.icon ? response.data.icon : null
|
this.tempIcon = response.data.icon ? response.data.icon : null
|
||||||
this.showQuickForm = true
|
this.showQuickForm = true
|
||||||
})
|
})
|
||||||
@ -315,9 +290,6 @@
|
|||||||
// set current temp icon as account icon
|
// set current temp icon as account icon
|
||||||
this.form.icon = this.tempIcon
|
this.form.icon = this.tempIcon
|
||||||
|
|
||||||
// Secret to base32 if necessary
|
|
||||||
this.form.secret = this.secretIsBase32Encoded ? this.form.secret : Base32.encode(this.form.secret).toString();
|
|
||||||
|
|
||||||
await this.form.post('/api/v1/twofaccounts')
|
await this.form.post('/api/v1/twofaccounts')
|
||||||
|
|
||||||
if( this.form.errors.any() === false ) {
|
if( this.form.errors.any() === false ) {
|
||||||
@ -359,7 +331,6 @@
|
|||||||
// Then the otp described by the uri
|
// Then the otp described by the uri
|
||||||
this.axios.post('/api/v1/twofaccounts/preview', { uri: this.uri }).then(response => {
|
this.axios.post('/api/v1/twofaccounts/preview', { uri: this.uri }).then(response => {
|
||||||
this.form.fill(response.data)
|
this.form.fill(response.data)
|
||||||
this.secretIsBase32Encoded = 1
|
|
||||||
this.tempIcon = response.data.icon ? response.data.icon : null
|
this.tempIcon = response.data.icon ? response.data.icon : null
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -441,9 +412,7 @@
|
|||||||
this.form.otp_type = to
|
this.form.otp_type = to
|
||||||
|
|
||||||
if (to === 'steamtotp') {
|
if (to === 'steamtotp') {
|
||||||
this.secretIsBase32Encoded = 1
|
|
||||||
this.form.service = 'Steam'
|
this.form.service = 'Steam'
|
||||||
this.form.secret = ''
|
|
||||||
this.fetchLogo()
|
this.fetchLogo()
|
||||||
}
|
}
|
||||||
else if (from === 'steamtotp') {
|
else if (from === 'steamtotp') {
|
||||||
|
@ -46,13 +46,6 @@
|
|||||||
<!-- secret -->
|
<!-- secret -->
|
||||||
<label :for="this.inputId('text','secret')" class="label" v-html="$t('twofaccounts.forms.secret.label')"></label>
|
<label :for="this.inputId('text','secret')" class="label" v-html="$t('twofaccounts.forms.secret.label')"></label>
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<p v-if="!secretIsLocked" class="control">
|
|
||||||
<span class="select">
|
|
||||||
<select @change="form.secret=''" v-model="secretIsBase32Encoded">
|
|
||||||
<option v-for="(format) in secretFormats" :key="format.value" :value="format.value">{{ format.text }}</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<p class="control is-expanded">
|
<p class="control is-expanded">
|
||||||
<input :id="this.inputId('text','secret')" class="input" type="text" v-model="form.secret" :disabled="secretIsLocked">
|
<input :id="this.inputId('text','secret')" class="input" type="text" v-model="form.secret" :disabled="secretIsLocked">
|
||||||
</p>
|
</p>
|
||||||
@ -141,7 +134,6 @@
|
|||||||
import Modal from '../../components/Modal'
|
import Modal from '../../components/Modal'
|
||||||
import Form from './../../components/Form'
|
import Form from './../../components/Form'
|
||||||
import OtpDisplayer from '../../components/OtpDisplayer'
|
import OtpDisplayer from '../../components/OtpDisplayer'
|
||||||
import Base32 from "hi-base32"
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -150,7 +142,6 @@
|
|||||||
counterIsLocked: true,
|
counterIsLocked: true,
|
||||||
twofaccountExists: false,
|
twofaccountExists: false,
|
||||||
tempIcon: '',
|
tempIcon: '',
|
||||||
secretIsBase32Encoded: null,
|
|
||||||
form: new Form({
|
form: new Form({
|
||||||
service: '',
|
service: '',
|
||||||
account: '',
|
account: '',
|
||||||
@ -176,10 +167,6 @@
|
|||||||
{ text: 9, value: 9 },
|
{ text: 9, value: 9 },
|
||||||
{ text: 10, value: 10 },
|
{ text: 10, value: 10 },
|
||||||
],
|
],
|
||||||
secretFormats: [
|
|
||||||
{ text: this.$t('twofaccounts.forms.plain_text'), value: 0 },
|
|
||||||
{ text: 'Base32', value: 1 }
|
|
||||||
],
|
|
||||||
algorithms: [
|
algorithms: [
|
||||||
{ text: 'sha1', value: 'sha1' },
|
{ text: 'sha1', value: 'sha1' },
|
||||||
{ text: 'sha256', value: 'sha256' },
|
{ text: 'sha256', value: 'sha256' },
|
||||||
@ -214,7 +201,6 @@
|
|||||||
const { data } = await this.axios.get('/api/v1/twofaccounts/' + this.$route.params.twofaccountId)
|
const { data } = await this.axios.get('/api/v1/twofaccounts/' + this.$route.params.twofaccountId)
|
||||||
|
|
||||||
this.form.fill(data)
|
this.form.fill(data)
|
||||||
this.secretIsBase32Encoded = 1
|
|
||||||
this.twofaccountExists = true
|
this.twofaccountExists = true
|
||||||
|
|
||||||
// set account icon as temp icon
|
// set account icon as temp icon
|
||||||
@ -235,9 +221,6 @@
|
|||||||
this.deleteIcon()
|
this.deleteIcon()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Secret to base32 if necessary
|
|
||||||
this.form.secret = this.secretIsBase32Encoded ? this.form.secret : Base32.encode(this.form.secret).toString();
|
|
||||||
|
|
||||||
await this.form.put('/api/v1/twofaccounts/' + this.$route.params.twofaccountId)
|
await this.form.put('/api/v1/twofaccounts/' + this.$route.params.twofaccountId)
|
||||||
|
|
||||||
if( this.form.errors.any() === false ) {
|
if( this.form.errors.any() === false ) {
|
||||||
|
@ -171,7 +171,6 @@
|
|||||||
otp_type: '',
|
otp_type: '',
|
||||||
icon: '',
|
icon: '',
|
||||||
secret: '',
|
secret: '',
|
||||||
secretIsBase32Encoded: 1,
|
|
||||||
algorithm: '',
|
algorithm: '',
|
||||||
digits: null,
|
digits: null,
|
||||||
counter: null,
|
counter: null,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user