#37What the Client Can Send
The class, the draft id, the event, every bound value — and what is checked.
4 min read · 36 of 38 in reading order
Every roundtrip is a POST the browser assembles, and the framework trusts nothing in it that it cannot verify. This is the list of what the browser chooses, what the framework checks on each item, and what is left to the app — the page a security review starts from, with the setting that decides each item named.
The class. The first request names the app: as ?app_start= on the page
URL, as a launchpad startup parameter, as a hash route, or in the body of the
first POST — after the start, no roundtrip carries a class name any more, and
nav_app_call( ) changes the app without the browser naming it. Before the
framework instantiates the class it checks one thing through RTTI: that the
class implements z2ui5_if_app. A class that does not is refused before it is
loaded. A class that does is started — for anybody who reaches the ICF node.
So every implementer of the interface on the system can be started by every
user of the node, and the decision which user may run which app is the app’s own
AUTHORITY-CHECK in the branch that shows the first view
(#22,
Authorization). A check on the node sees only
who reaches the framework, not which app runs.
The draft id. Every response carries the id of the row in z2ui5_t_01
that holds the serialized instance, and the next request sends it back. The
row carries sy-uname, and the read is scoped to it: an id of another user’s
draft is answered with NO_DRAFT_ENTRY_OF_PREVIOUS_REQUEST_FOUND, not with
that user’s instance. An expired id — older than draft_exp_time_in_hours,
four hours by default — is gone, because every first start of an app deletes
every row past the expiry. What a draft id therefore gives its holder is the
ability to continue their own session from a bookmark or a copied link, and
nothing about anybody else’s.
The event, and every bound value. The event name arrives from the browser,
so get_event( ) returns whatever the browser sent — a button that the view
did not render, or rendered disabled, is no guarantee that its event never
arrives. A handler that must not run for a given state checks the state, not
the button. The same holds for the model: every attribute the view binds with
_bind( ) is a path the browser may send a value for, and the framework
writes what arrives into the attribute, a Text as much as an Input. A
value that must not change on the way through the browser — a key, a price,
a user name — is not bound, or is re-read on the server before it is used.
Startup parameters. client->get( )-t_comp_params holds the parameters
of the target mapping or the start URL, and the user can start the app with any
other value. An app that shows one sales order checks that the user may see
that order, not only that they may run the app
(Startup parameters are user input).
The headers. The CSRF check compares the host of the Origin header — or
Referer, when there is no Origin — against the request’s Host, and a
cross-origin POST is rejected before any app logic runs. It is on by default
(check_csrf_active), and it is lenient by design about a request that carries
neither header. Behind a reverse proxy or a web dispatcher the host the
browser saw is the first X-Forwarded-Host, so that header is trusted by
default (check_trust_forwarded_host). A client can set that header too: an
installation that is not behind a proxy switches the trust off in its user
exit, and only the transport-level Host is compared from then on
(CSRF).
The error body. An exception that escapes main( ) ends the roundtrip
with HTTP 500, and the body carries the exception chain, source positions and
system context. check_hide_error_details in the user exit reduces it to a
bare Internal Server Error
(#20); a production system sets
it.
And the other direction. follow_up_action( ) with
cs_event-control_by_id lets the server call a method on a control in the
browser, and the frontend refuses a list of methods that would break its own
invariants — destroy, fireEvent, clone, the bind…, attach… and
setModel families, the generic setAggregation and its kin — and casts each
argument to the kind the method declares, dropping arguments beyond them. That
list is a guard for the framework, not a boundary against the client: the
actions flow from the server to the browser, and the browser is the untrusted
side. A security review of the client reads the six items above; this one
belongs to a review of the app’s own follow-up actions.
What the browser never gets is the business logic. It receives a view and the
data bound to it (#5), and every
decision about either is taken in main( ) on the server, on the next
request.