Closed Fipschen closed 1 month ago
Hi, I don't know much about Angular's reactive forms, but in general when attaching event listener with addEventListener
you have to do it after the altcha
script loads, for example in the window.addEventListener('load', ...)
handler. This has something to do with WebComponents where the events just don't attach before the component loads.
For validation - the widget creates a hidden input with name="altcha"
(or other configured name
) so you can use a query selector input[name="altcha"]
to get the element and check if it's checked, probably better solution if you don't need events.
Of course, full validation has to be done on the server, but it would be nice if the altcha-widget worked like other input widgets with angular forms and set and cleared the form.invalid field. I could just add [disabled]="form.invalid" to my submit button and not worry about eventlistening.
Hi,
I've made it to get the value of name=altcha, but only with document.getElementsByName('altcha')[0]
. With that I couldn't add an EventListener, with document.querySelect('#altcha')
I didn't get an element, only null.
I'll going on to try to get an solution and will report here.
Thank you
Fipschen
Hi again,
now I'd found a workaround:
#form.component.html
<form [formGroup]="form" (ngSubmit)="sendForm()" class="text-center">
<!-- email address -->
<mat-form-field [style.width.px]="480">
<mat-label>Email Address</mat-label>
<input type="text" formControlName="emailAddress" maxlength="75" placeholder="email@address.com" matInput />
</mat-form-field>
<altcha-widget
[strings]=spamProtectionService.getAltchaConfig()
auto="onload"
[challengejson]=spamProtectionService.getAltchaChallenge()
(statechange)="altchaStateChange($event)">
</altcha-widget>
<br />
<!-- Submit button -->
<button type="submit" [disabled]="form.invalid || !formAltchaVerified" class="btn btn-primary btn-block mb-4">send</button>
</form>
form.component.ts
export class FormComponent
{
protected form: FormGroup;
protected formAltchaVerified: boolean = false;
constructor(
protected spamProtectionService: SpamProtectionService,)
{
this.form = new FormGroup({
emailAddress: new FormControl(null, [Validators.required, Validators.email]),
});
}
altchaStateChange(event: any): void
{
if (event.detail.state == "verified")
{
this.formAltchaVerified = true;
}
}
So it's not really a part of the form, but it has an effect of the submit button.
Thank you very much
Fipschen
I am using version: "altcha": "^0.6.5", and do not have 'statechange' event available. Change is available, but there is no details object in the $event. @Fipschen , this solution may actually be enough. I am validating on the server anyway and just the presence of the ALTCHA widget may be restrictive enough for my needs. Thanks
I'm using the same version of altcha, for me it work's, tested a lot of times.
That's only a solution for the frontend and I'll also change the challengejson to challengeurl when I get how to realise this on php symfony. The backend will get another spam protection or the M2M-Feature. At the moment for me it's important the get the side ready with basic security and doing the security better later.
I am using version: "altcha": "^0.6.5", and do not have 'statechange' event available. Change is available, but there is no details object in the $event. @Fipschen , this solution may actually be enough. I am validating on the server anyway and just the presence of the ALTCHA widget may be restrictive enough for my needs. Thanks
The statechange
doesn't fire probably because the addEventListener
must be called after the altcha script loads and the widget renders - it's some limitation with Web Components; I'll look into integrating with Angular and what could be improved here.
I'm using the same version of altcha, for me it work's, tested a lot of times.
That's only a solution for the frontend and I'll also change the challengejson to challengeurl when I get how to realise this on php symfony. The backend will get another spam protection or the M2M-Feature. At the moment for me it's important the get the side ready with basic security and doing the security better later.
You were correct. The only problem was my IDE, IntelliJ, telling me that there was no statechange event.
Much thanks.
@RickPoleshuck VS Code also told me there is no statechange. I'd found it with console.log() in the Browser (just as an tip 😬).
I am now having problems converting the pseudo-code for the challenge to Java. Are there any samples?
Also, the provided API challenge is returning a maxnumber in the JSON that is not documented. Is maxnumber the same as the complexity value, maximum secret number?
{ "algorithm": "SHA-256", "challenge": "69960f0e2d000eb8cf543cf4fa06881ddf79d4a22ed42b1863b9124236ab9536", "maxnumber": 20000, "salt": "129188ee83c58246f45fab15?expires=1722109713", "signature": "e323b1d95c91396d66a81a186b35e20010ea51ea8fee769c096eb24a21760c47" }
I'm don't know much about Java. In the Docs are this official integrations for other languages: https://altcha.org/de/docs/integrations/ I can show you also my code for the integration in PHP:
$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$chars_length = strlen($chars);
$random_string = "";
$length = 16;
for ($i = 0; $i < $length; $i++)
{
$random_string .= $chars[random_int(0, $chars_length - 1)];
}
$salt = $random_string;
$algorithm = "sha256";
$hmac_key = "sampleKey";
$secret_number = random_int(10000, 150000);
$challende = hash($algorithm, $salt.$secret_number);
$signature = hash_hmac($algorithm, $challende, $hmac_key);
$return = json_encode([
'salt' => $salt,
'challenge' => $challende,
'algorithm' => $algorithm,
'signature' => $signature, ]);
The content of $return I'd took in the challengejson.
My problem is to verify the challenge, but I didn't really tried this until now, I'll do this later. I hope my code could help you.
Much thanks. Apparently, the maxnumber returned by the API challenge is not necessary.
I would very strongly recommend you make use of the expires feature for production.
Looks like that, yes. I'm glad, I could help you.
Yes, I'll do this, thank you. I'm sorry, my integration is as an class, for easier reading I wrote it as short version.
I am now having problems converting the pseudo-code for the challenge to Java. Are there any samples?
Also, the provided API challenge is returning a maxnumber in the JSON that is not documented. Is maxnumber the same as the complexity value, maximum secret number?
{ "algorithm": "SHA-256", "challenge": "69960f0e2d000eb8cf543cf4fa06881ddf79d4a22ed42b1863b9124236ab9536", "maxnumber": 20000, "salt": "129188ee83c58246f45fab15?expires=1722109713", "signature": "e323b1d95c91396d66a81a186b35e20010ea51ea8fee769c096eb24a21760c47" }
The maxnumber
parameter is optional and it refers to the maximum number used to generate the random number. It helps the widget to better distribute work for multiple workers making it run faster in most cases, so it's recommended to return it. Its indeed missing in the docs, I'll add it.
The Java library will be available soon, until then you can try to generate code in Java with LLMs using for example the python library, that should be the most accurate.
I just updated the angular demo, it contains a new component altcha
which can be used as a form control with validation. This should solve the problems with event listeners and it also provides the payload as the control's value.
Thank you for the altcha component. After debugging my own bug it work's for me 👍
The Java library will be available soon, until then you can try to generate code in Java with LLMs using for example the python library, that should be the most accurate.
Maybe my failed attempt at writing a Java service to provide a challenge will be helpful. Maybe you can point out my error. :-)
I use the attached classes like this:
@GetMapping("/challenge")
public ResponseEntity
Thank you for the altcha component. After debugging my own bug it work's for me 👍
Did you do something other than add 'required' like this:
Thanks.
I discovered what I believe my real problem is. The new widget does not send a message to the form to recheck the validation. If I click on the ALTCHA first the form works correctly, but if I click on the ALTCHA last then the form doesn't get notified to recheck the validation. I know this is an Angular problem and not a ALTCHA problem, but...
Much thanks again.
@RickPoleshuck The Java library is out: https://github.com/altcha-org/altcha-lib-java, it's not in Maven Central yet, but it's installable with JitPack. Please report any issues.
@RickPoleshuck The Java library is out: https://github.com/altcha-org/altcha-lib-java, it's not in Maven Central yet, but it's installable with JitPack. Please report any issues.
Thanks. I have made a pull request for the Readme.
Hi there,
I'll try to build the php backend for challenge generation, but I'll get an CORS-Error:
Cross-source (cross-origin) request blocked: The same-source rule prohibits reading the external resource at https://localhost:8000/funktion/altcha/open-challenge. (Reason: Header ‘x-altcha-spam-filter’ is not permitted due to the header ‘Access-Control-Allow-Headers’ from the CORS preflight response).
Header send:
OPTIONS /funktion/altcha/open-challenge HTTP/2 Host: localhost:8000 User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:130.0) Gecko/20100101 Firefox/130.0 Accept: / Accept-Language: de,en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate, br, zstd Access-Control-Request-Method: GET Access-Control-Request-Headers: x-altcha-spam-filter Referer: http://localhost:4200/ Origin: http://localhost:4200 Connection: keep-alive Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-site Priority: u=4 TE: trailers
Header anwser:
HTTP/2 200 access-control-allow-headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method, Authorization access-control-allow-methods: GET, POST, OPTIONS, PATCH, DELETE access-control-allow-origin: http://localhost:4200 allow: GET, POST, OPTIONS, PATCH, DELETE content-type: text/html; charset=UTF-8 date: Tue, 30 Jul 2024 04:15:59 GMT x-powered-by: PHP/8.3.9 X-Firefox-Spdy: h2
I can't find something in the docs about the x-altcha-spam-filter header. What do I need to do? I'll also send me integration for php symfony7 and plain php here.
I discovered what I believe my real problem is. The new widget does not send a message to the form to recheck the validation. If I click on the ALTCHA first the form works correctly, but if I click on the ALTCHA last then the form doesn't get notified to recheck the validation. I know this is an Angular problem and not a ALTCHA problem, but...
I can't fully confirm this. If I do the altcha at last in the form I need to click somewhere outside the altcha-widget to change the validation state. But that's the same like on every other form input.
Thank you
Hi,
CORS: it's mention in the error message, that the header x-altcha-spam-filter
isn't listed in Access-Control-Allow-Headers
(which is the server response to CORS), so just add it there. I'll also add documentation about this.
I can't fully confirm this. If I do the altcha at last in the form I need to click somewhere outside the altcha-widget to change the validation state. But that's the same like on every other form input.
I think validation works fine in the angular demo, or is it reproducible also there?
@Fipschen , If you were using nginx in front of your webserver, you would add something like this to your config:
if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods GET,HEAD,OPTIONS,POST,PUT,DELETE; add_header Access-Control-Allow-Headers Origin,X-Requested-With,‘X-Altcha-Spam-Filter,Content-Type,Accept,Authorization; return 204; } CORS is an annoyance, it is designed to poorly protect the user, not the web site. I know how to fix it with nginx. You need to find the documentation for CORS for your particular web server framework.IMO, ALTCHA cannot possibly document all the possible solutions. Sorry, I can't be more helpful.
I think validation works fine in the angular demo, or is it reproducible also there? Yes, your demo works correctly. I might have to switch from TemplateForm to ReactiveForms.
I know what CORS is, it has already cost me a lot of nerves, is just didn't get to add this in control 🤦♂️ I'm sorry, now it work's fine.
The Angular Demo works fine for me. I'll just used the json-challende til now, but the next days I would code the backend part, report again and show you my implementation. I'm not fit in GitHub, so I can't do a pull request.. to learn is on my to-do-list.. sorry
The App-Altcha widget works fine in my first app, which is not yet ready for production. But I am having trouble with my second Angular app. The widget is there but zero by zero size and not visible. The first app is based on the jHipster framework and the non working app is just raw Angular. I would be happy to share my code with @ovx , but not with the world. Daniel, I am hoping this is an opportunity to look at addressing future issues for other users. But let me know if I am asking too much. https://www.linkedin.com/in/rickpoleshuck/
What is interesting is that our production app has not received one bogus message in a year, but my 'toy' app which is not advertised at all has had about 20 bogus messages. Altcha has addressed that problem on my 'toy' app already. Thanks again.
Here is my working app: https://fterm.tech/#/contact. If you try it out, please leave a comment that says why.
My backend work's now. Now I have one question left: I'd read, it's better for security to check the result of the widget. I thought til now the widget would send an second request to validate the solution, but my widget doesn't it. Did this tip mean to send the solution with the form data and check it there? I'm sorry for that stupid questions, my english isn't the best.. 😅
Thank you
My backend work's now. Now I have one question left: I'd read, it's better for security to check the result of the widget. I thought til now the widget would send an second request to validate the solution, but my widget doesn't it. Did this tip mean to send the solution with the form data and check it there? I'm sorry for that stupid questions, my english isn't the best.. 😅
Thank you
The widget creates a payload (a base64 encoded string) and send's it together with the form data as altcha
field (if you're using custom ajax handler, add the altcha payload to the submitted data). You have to validate it on the server using the secret HMAC key - otherwise anyone could just bypass it. So there's no second call from the widget, once validated, the payload is all you need to verify it on the server in the submission handler.
The widget creates a payload (a base64 encoded string) and send's it together with the form data as
altcha
field (if you're using custom ajax handler, add the altcha payload to the submitted data). You have to validate it on the server using the secret HMAC key - otherwise anyone could just bypass it. So there's no second call from the widget, once validated, the payload is all you need to verify it on the server in the submission handler.
Okay, thank you very much. I'll finish the backend next days and send it than. Thank you
I am curious what html error you plan on throwing if the validation of the altchaChallenge fails. Right now I am throwing a BadRequest, but maybe that is giving hackers too much information. One thought is to retain the connection for maybe 60 seconds and then throw a timeout exception.
I'll handle it like wrong validation error, like an bad login request or post request with invalid data. I think hackers can't do much with that.
I've a problem again with the widget: When using the expire-param the widget can't solve the challenge.
#Console
ALTCHA mounted 0.6.5
ALTCHA workers 12
ALTCHA auto onload
ALTCHA fetching challenge from https://localhost:8000/funktion/altcha/open-challenge
ALTCHA expire 599050
ALTCHA challenge
Object { id: null, _create_datetime: "2024-08-01T18:32:04+02:00", salt: "54Di61GGB9BpN2Iy?expires=1722530524", challenge: "f535c272e5fe15df15b3cec484275cbf7c1fcf0c071ee5211748bf9447d02ce2", algorithm: "SHA-256", signature: "f72e653cb790360f4bf2dafe6e7a068c39c2b94204299fd7996ceeb515279431", secret_number: 147224, maxnumber: 150000 }
ALTCHA solution null
ALTCHA Unable to find a solution. Ensure that the 'maxnumber' attribute is greater than the randomly generated number.
ALTCHA Error: Unexpected result returned.
#Challenge form Server
{
{"id": null,
"_create_datetime":"2024-08-01T18:32:04+02:00",
"salt":"54Di61GGB9BpN2Iy?expires=1722530524",
"challenge":"f535c272e5fe15df15b3cec484275cbf7c1fcf0c071ee5211748bf9447d02ce2",
"algorithm":"SHA-256",
"signature":"f72e653cb790360f4bf2dafe6e7a068c39c2b94204299fd7996ceeb515279431",
"secret_number":147224,
"maxnumber":150000}
}
(I've added the secret number for debugging only).
When opening the widget it says immediately Verification expired. Try again.
. After a little time it goes to Verification failed. Try again later.
and returns the last 3 lines in the console.
Do I set the param wrong? I'd adopted it from the docs https://altcha.org/docs/server-integration/#salt-parameters 🤔
Are you getting the time in seconds? A very common mistake is to think a unix timestamp is milliseconds.
I'm using the unix timestamp in seconds, php has milliseconds only with * 1000
.
The console print this: ALTCHA expire 599029
(10 minutes)
But why fails the validation by the parameter?
The current unix timestamp is 1722535587 + (10 * 60) = 1722536187 This is the number of seconds since January 1, 1970 + the increment of 10 seconds. Way more than 599029 seconds. I am betting you are getting the number of seconds your server has been running, about 7 days, rather than the unix timestamp.
In the challenge in the salt is the timestamp: "salt":"54Di61GGB9BpN2Iy?expires=1722530524"
599029 is the expire-time in milliseconds, the widget returns that in console.
In the challenge in the salt is the timestamp:
"salt":"54Di61GGB9BpN2Iy?expires=1722530524"
599029 is the expire-time in milliseconds, the widget returns that in console.
That timestamp is almost two hours ago. About 45 minutes before your first post on the topic. The expires option works fine for me. But I did lose my bet. :-)
The 2 hours should be the timezone. The widget calculate the expire output in console right, that couldn't be the mistake. Adding 2 h (7 200 seconds) the output is + 7 200 000 and the same error. Could you please show me a salt with expire param from you?
A Unix timestamp, also known as Unix time or POSIX time, is a numerical value that represents the number of seconds that have passed since midnight UTC on January 1, 1970, also known as the Unix epoch.
No timezone should be included.
Here is one that works for me:
cd115b6c9117da1d255653bb99cf9b7bcc0a94dedd1482ad?expires=1722542885
This is a useful website for working with unix timestamps: https://www.unixtimestamp.com/
I'll handle it like wrong validation error, like an bad login request or post request with invalid data. I think hackers can't do much with that.
I've a problem again with the widget: When using the expire-param the widget can't solve the challenge.
#Console ALTCHA mounted 0.6.5 ALTCHA workers 12 ALTCHA auto onload ALTCHA fetching challenge from https://localhost:8000/funktion/altcha/open-challenge ALTCHA expire 599050 ALTCHA challenge Object { id: null, _create_datetime: "2024-08-01T18:32:04+02:00", salt: "54Di61GGB9BpN2Iy?expires=1722530524", challenge: "f535c272e5fe15df15b3cec484275cbf7c1fcf0c071ee5211748bf9447d02ce2", algorithm: "SHA-256", signature: "f72e653cb790360f4bf2dafe6e7a068c39c2b94204299fd7996ceeb515279431", secret_number: 147224, maxnumber: 150000 } ALTCHA solution null ALTCHA Unable to find a solution. Ensure that the 'maxnumber' attribute is greater than the randomly generated number. ALTCHA Error: Unexpected result returned.
#Challenge form Server { {"id": null, "_create_datetime":"2024-08-01T18:32:04+02:00", "salt":"54Di61GGB9BpN2Iy?expires=1722530524", "challenge":"f535c272e5fe15df15b3cec484275cbf7c1fcf0c071ee5211748bf9447d02ce2", "algorithm":"SHA-256", "signature":"f72e653cb790360f4bf2dafe6e7a068c39c2b94204299fd7996ceeb515279431", "secret_number":147224, "maxnumber":150000} }
(I've added the secret number for debugging only). When opening the widget it says immediately
Verification expired. Try again.
. After a little time it goes toVerification failed. Try again later.
and returns the last 3 lines in the console. Do I set the param wrong? I'd adopted it from the docs https://altcha.org/docs/server-integration/#salt-parameters 🤔
Hi,
I know what an unix timestamp is and it's calculated right in my integration (checked with online-tools).
When I'm add ?expires=$timestamp
to the salt (after hashing) the widget print an output in the console ALTCHA expire 559754
, I think it's the left time before is expired. If I add it to the salt before hashing, the widget don't print ALTCHA expire ...
.
So I think the parameter is added right? My syntax of the salt looks like Rick ones.
I know what an unix timestamp is and it's calculated right in my integration (checked with online-tools).
When I'm add
?expires=$timestamp
to the salt (after hashing) the widget print an output in the consoleALTCHA expire 559754
, I think it's the left time before is expired. If I add it to the salt before hashing, the widget don't printALTCHA expire ...
. So I think the parameter is added right? My syntax of the salt looks like Rick ones.
The parameter is right; I checked your challenge you posted and the hash doesn't match, so there's something wrong on the server when generating the challenge: the expected hash for salt + number
(54Di61GGB9BpN2Iy?expires=1722530524147224
) is 94c03c51c1d701a535aa62dbbac9a25d5549b48eaf22a378e32cef6b5b049f7c
.
Oh hell... now I'd found the error: I always add the expires-param to the salt OR the challenge, not to both.. I thought only the salt need it for export to the widget.. 🤦♂️🤦♂️ I'm sorry and thank you very much for your help
Oh hell... now I'd found the error: I always add the expires-param to the salt OR the challenge, not to both.. I thought only the salt need it for export to the widget.. 🤦♂️🤦♂️ I'm sorry and thank you very much for your help Too be a little clearer for other readers: Any options added to the salt, including the expires option, must be done before generating the challenge hash value.
Hi there, now I've the integration of server side in plain php finish, added as attachments. For Symfony I have to look for how to create libs first.
Have a nice weekend
Hey there,
thank you for this open source solution. I've a problem to use it in an angular 17 reactive forms. I'd seen the stackblitz-code, where the widget is included in the validation of the form. I can't see where the widget it bounded to the form to check the validation. I can't adopt it on myself. My form looks like this:
The
document.querySelector('#altcha').addEventListener
havn't an effect. Could soneone help me?Thank you very much