return function () {
$self = url('/@object/@action/' . urlencode('security/csrf'));
$form_id = 'docs_csrf_demo';
$intro = '<p class="docs-demo-intro">CSRF protection of form2: with the <code>csrf</code> option the form carries a one-time
<code>_token</code> that <code>yf_csrf_guard</code> keeps per form in the session. <b>Send with the token</b> passes; <b>Send without a
token</b> posts the same fields (with this form\'s <code>__form_id__</code>) minus the token, as a forged cross-site form would,
and is refused; <b>Replay the used token</b> resends a token that was already accepted and is refused too, because
<code>validate()</code> consumes it. Nothing is stored. Classes: <code>yf_form2</code>, <code>yf_csrf_guard</code>.</p>';
$result = '';
$form = form((array) $_POST, ['csrf' => true, '__form_id__' => $form_id])
->validate(['__form_id__' => $form_id, 'message' => 'trim|required|max_length[200]'])
->on_validate_ok(function ($data) use (&$result) {
// the token was valid (a CSRF failure adds a validation error, so this is not reached then)
$_SESSION['docs_csrf_demo_used'] = (string) ($_POST['_token'] ?? '');
$result = '<div class="alert alert-success"><b>Accepted.</b> The token matched and is now used up. Message: '
. _prepare_html((string) $data['message'], 0) . '</div>';
})
->text('message', ['value' => 'Hello', 'maxlength' => 200])
->submit(['value' => 'Send with the token']);
// A failed CSRF check logs into the log_csrf_errors table; the demo writes no rows
$guard = _class('csrf_guard');
$log_errors = $guard->LOG_ERRORS;
$guard->LOG_ERRORS = false;
try {
$html = (string) $form;
} finally {
$guard->LOG_ERRORS = $log_errors;
}
if (is_post() && $result === '' && ($_POST['__form_id__'] ?? '') === $form_id) {
$result = '<div class="alert alert-danger"><b>Refused.</b> The form was posted without a valid token; form2 showed the error and ran no on_validate_ok() handler.</div>';
}
$forged = '<form method="post" action="' . $self . '" class="docs-demo-forged">'
. '<input type="hidden" name="__form_id__" value="' . $form_id . '"><input type="hidden" name="message" value="Forged without a token">'
. '<button type="submit" class="btn btn-danger">Send without a token</button></form>';
$used = (string) ($_SESSION['docs_csrf_demo_used'] ?? '');
$replay = $used === '' ? '' : '<form method="post" action="' . $self . '" class="docs-demo-forged">'
. '<input type="hidden" name="__form_id__" value="' . $form_id . '"><input type="hidden" name="message" value="Replayed">'
. '<input type="hidden" name="_token" value="' . _prepare_html($used, 0) . '">'
. '<button type="submit" class="btn btn-warning">Replay the used token</button></form>';
css('.docs-demo-forged { display:inline-block; margin:0 6px 15px 0; }');
return $intro . $result . $html . '<h4>Forged requests</h4>' . $forged . $replay;
};