What are best practices for checking user provided data for config files?

Hello. I am developing my telegram-bot and now in need of understanding how I do “server-side”, if I may say so, of user provided data for config file.

In my case user can insert bot token to /etc/config/tgbot config file via router web-interface (I have develped my simple form for this).

I can do “client-side” check, of course, via JavaScript, but I wouild not want evil hackers send wrong token string to my config file. So, how do I check that token is in proper format? Via UBUS? Or rpcd has this functionality? Or I store it in some variable and get via some mechanism like SQL stored procedures?

Many Thanks!

Isn't that a question for Telegram?
If they'd tell you, wouldn't it mean it could be used to generate "random" keys, allowing SPAM messages to be sent ?

The whole concept of your router trying to parse messages and changing its configuration based on that from telegram is a problem in the first place.

Guys, no, I guess I ask my question in a wrong way.

I just need to know what mechanism I use to check user provided values in form, Telegram token is just example.

For example, I have a form field in luci web-intreface, like

Enter your user data here [_____________________]

Then user provide some data, like

Enter your user data here [_user data_to_store_in_config_file_]

then this data via form are send to /etc/config/someconfigfile

I just need some mechanism to check data BEFORE they writen to config. I mean, I can check it in JavaScript form, but it is not enough. I need some validation mechanism which recieve user data and then validate in OpenWRT router program space itself on “server-side”, not JavaScript which is “client-side”. Like SQL validation when server recieve data and validate it first before writing to database

P.S. I do not need to parse any telegram messages going through router.

You can use PHP on openwrt, LUA or simple CGI (shell script). Up to your knowledge or preference.

1 Like

PHP is client-side, I guess? Anyway, I do not know it too good. I would prefer to use shell scripts, but I was thinking may be there are built-in mechanism for “server side data validation”? I was thinking maybe I can validate data via ubus, or rpcd somehow. But if there are none of such methods I guess my only option is to use some shell script as deamon which will validate userdata recieved from web-form.

P.S. By the way I thought that openwrt frontend is JavaScript. There is PHP avialable without any additional installations, just out-of-the-box?

Yep, just look in the LuCI repo with tons of app examples. There's also an API description https://openwrt.github.io/luci/jsapi/index.html ... look for validate or datatype. Just use OpenWrt standards - PHP & Co. is bloated & needless.

1 Like

PHP is THE server side. Must be installed as additional package on openwrt; can be used by default (very simple) uhttpd web server. Or you also install nginx /apache for many more options. For rather simple server side usage (on openwrt box), CGI script(s) (i.e. shell scripts) in combo with uhttpd might suffice.

1 Like

The way would be to have initial validation performed within your javascript, and then have that call a ucode validation handler that also performs validation before doing the uci persistance.

Currently it sounds like you have javascript => uci.
Make this instead javascript => ucode => uci
UCode Integration | openwrt/uhttpd | DeepWiki

If you want you can bring in PHP and do it there.... but I wouldn't.

1 Like

I surely do not want any PHP & Co. :slight_smile: many thanks for your help.

many thanks for your help!

One moment please. I just wonder why example form from example-app uses approach when data stored directly from js-form to config?

'use strict';


require 'view'
require 'form'


return view.extend({
    render: function() {
        let m, s, o;


        m = new form.Map('example', _('Example Form'), _('Example Form Configuration.'));


        s = m.section(form.TypedSection, 'first', _('first section'));
        s.anonymous = true;
        s.option(form.Value, 'first_option', _('First Option'), _('Input for the first option'));


        s = m.section(form.TypedSection, 'second', _('second section'));
        s.anonymous = true;


        o = s.option(form.Flag, 'flag', _('Flag Option'), _('A boolean option'));
        o.default = '1';
        o.rmempty = false;


        o = s.option(form.ListValue, 'select', _('Select Option'), _('A select option'));
        o.placeholder = 'placeholder';
        o.value('key1', 'value1');
        o.value('key2', 'value2');
        o.rmempty = false;
        o.editable = true;


        s = m.section(form.TypedSection, 'third', _('third section'));
        s.anonymous = true;


        o = s.option(form.Value, 'password_option', _('Password Option'), _('Input for a password (storage on disk is not encrypted)'));
        o.password = true;


        o = s.option(form.DynamicList, 'list_option', _('Dynamic list option'));


        return m.render();
    }
});

I am sorry for raising this question again but it seems pretty important to me.

From this topic What are best practices for checking user provided data for config files? - For Developers - OpenWrt Forum I understand that safest way to store data from webform to config file is a chain javascript => ucode => uci but in example app, as I noticed, there is direct writing to config from webform. Also I look through adblock script for adding sites to whitelist and see that it usese direct writing to file from webform!

'use strict';
'require view';
'require fs';
'require ui';

let localFile = '/etc/adblock/adblock.allowlist';
let notMsg, errMsg;

return view.extend({
    load: function () {
        return L.resolveDefault(fs.stat(localFile), "").then(function (stat) {
            if (!stat) {
                return fs.write(localFile, "");
            }
            return Promise.all([
                L.resolveDefault(fs.stat(localFile), ""),
                L.resolveDefault(fs.read_direct(localFile), "")
            ]);
        });
    },

    render: function (allowlist) {
        if (allowlist[0] && allowlist[0].size >= 100000) {
            document.body.scrollTop = document.documentElement.scrollTop = 0;
            ui.addNotification(null, E('p', _('The allowlist is too big, unable to save modifications.')), 'error');
        }
        return E('div', { 'class': 'cbi-section cbi-section-descr' }, [
            E('p', _('This is the local adblock allowlist to always-allow certain domains.<br /> \
                <em><b>Please note:</b></em> add only one domain per line. Comments introduced with \'#\' are allowed - ip addresses, wildcards and regex are not.')),
            E('textarea', {
                'style': 'width: 100% !important; padding: 5px; font-family: monospace; margin-top: .4em',
                'spellcheck': 'false',
                'wrap': 'off',
                'rows': 25
            }, [allowlist[1] != null ? allowlist[1] : ''])
        ]);
    },

    handleSave: function (ev) {
        let value = ((document.querySelector('textarea').value || '').trim().toLowerCase().replace(/\r\n/g, '\n')) + '\n';
        return fs.write(localFile, value).then(function () {
            document.querySelector('textarea').value = value;
            document.body.scrollTop = document.documentElement.scrollTop = 0;
            if (!notMsg) {
                ui.addNotification(null, E('p', _('Allowlist modifications have been saved, reload adblock that changes take effect.')), 'info');
                notMsg = true;
            }
        }).catch(function (e) {
            document.body.scrollTop = document.documentElement.scrollTop = 0;
            if (!errMsg) {
                ui.addNotification(null, E('p', _('Unable to save modifications: %s').format(e.message)), 'error');
                errMsg = true;
            }
        });
    },

    handleSaveApply: null,
    handleReset: null
});

I am a bit confused and in need of some explanation if you please. What approach I have to use? Also, I do not understand which server-side environment is going to be deprecated in future OpenWRT versions: UCODE or Lua?

Thank you.

Why on earth did this merit a second thread.

because it's an example of how to create different luci graphical objects... I think you're expecting too much from an example.

1 Like

ok, thank you. it is now clear to me that only proper way to store userdata from webform is via chain you specify: javascript => ucode => uci

Sorry, I was just a bit in misunderstanding. Topic can be closed.

1 Like

I can't think of any existing applications / modules that consider they will have an adversarial user unlike you're expecting with your evil hackers. Data entry for most (all that I'm aware of) applications / modules occurs via the javascript UI, and hence 'soft validation' at that point was considered sufficient.

If your clients will be untrusted, and may not be constrained to just the UI you present to them, then you will want to provide additional security beyond that provisioned in the javascript.

Lua is being very slowly deprecated, in favour of ucode (almost the same as javascript, but server-side).

You could reference this..

it's the only server side validation that I could find... it's in Lua, where for new things you'd ideally want to target ucode... if you google you can find the references indicating OpenWRT is moving away from Lua and towards ucode for this stuff.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.