Nápad na bakalářskou práci se Spring

Re:Nápad na bakalářskou práci se Spring
« Odpověď #45 kdy: 03. 07. 2019, 21:58:54 »
Pokud běží REST API na jiném hostname, než webová aplikace (typicky to tak je), musí ten REST server posílat Access-Control-* hlavičky, aby prohlížeč komunikaci akceptoval. Je to ochrana před tím, aby webová stránka nemohla volat REST služby serveru, který s tím nepočítá – mohla by tak na ten server útočit.

Akorát ve Springu to nemusíte řešit ručně, má už pro to podporu: Enabling Cross Origin Requests for a RESTful Web Service.


Re:Nápad na bakalářskou práci se Spring
« Odpověď #46 kdy: 04. 07. 2019, 15:30:17 »
Takže to mé řešení je v pohodě, že? :-)

Nedaří se mi zjistit, jak správně podepsat to JWT.

Na tom autorizačním serveru se mi to podepíše v pohodě (zkoušel jsem to přes tu stránku jwt.io a podpis se ověří při použití public key), ale v aplikaci nevím, jak udělat to ověření na tom resource serveru...
Zkoušel jsem i auth0, tam je problém, že nevím, jak:

1) Vložím vytvořený token tím auth0 do té odpovědi...
2) Při ověření na resource serveru bych potřeboval přístup k tomu tokenu ve stringové formě, abych to mohl přes tu jejich funkci ověřit... na to jsem také zatím nepřišel, jak na resource serveru můžu dostat přístup k access_tokenu, který tam zrovna přišel...

Ten podpis a ověření v tom mém kodu probíhá u accessTokenConverteru.

Kód: [Vybrat]
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private static final String GRANT_TYPE_PASSWORD = "password";
    private static final String AUTHORIZATION_CODE = "authorization_code";
    private static final String REFRESH_TOKEN = "refresh_token";
    private static final String SCOPE_READ = "read";
    private static final String SCOPE_WRITE = "write";
    private static final String TRUST = "trust";
    private static final int VALID_FOREVER = -1;

    @Autowired
    private AuthenticationManager authManager;
   
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .inMemory()
                .withClient(Const.CLIENT_ID)
                .secret(Const.CLIENT_SECRET)
                .authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN)
                .scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
                .accessTokenValiditySeconds(500)
                .refreshTokenValiditySeconds(VALID_FOREVER);
    }
   
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                if(authentication.getOAuth2Request().getGrantType().equalsIgnoreCase("password")) {
                    final Map<String, Object> additionalInfo = new HashMap<String, Object>();
                    additionalInfo.put("organization", "NEJAKA INFORMACE");
                    ((DefaultOAuth2AccessToken) accessToken)
                            .setAdditionalInformation(additionalInfo);

                }
                accessToken = super.enhance(accessToken, authentication);
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(new HashMap<>());
                return accessToken;
            }
        };

       KeyStoreKeyFactory keyStoreKeyFactory =
                new KeyStoreKeyFactory(new ClassPathResource("test.jks"), "password".toCharArray());
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("test"));

        return converter;
    }
   
   
   
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter()));

        endpoints.tokenStore(tokenStore())
                .tokenEnhancer(accessTokenConverter())
                .accessTokenConverter(accessTokenConverter())
                .authenticationManager(authManager);
    }
}

Kód: [Vybrat]
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private static final String RESOURCE_ID = "resource_id";

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID).stateless(false);
        resources.tokenStore(tokenStoree());
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.
                anonymous().disable()
                .authorizeRequests()
                .antMatchers("/aadmin/**").authenticated()
                .and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());

        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverterr() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        Resource resource = new ClassPathResource("public.txt");
        String publicKey = null;
        try {
            publicKey = IOUtils.toString(resource.getInputStream());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        converter.setVerifierKey(publicKey);

        return converter;
    }

    @Bean
    public DefaultTokenServices tokenServices(final TokenStore tokenStore) {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStoree());
        return tokenServices;
    }


    @Bean
    public TokenStore tokenStoree() {
        return new JwtTokenStore(accessTokenConverterr());
    }

Re:Nápad na bakalářskou práci se Spring
« Odpověď #47 kdy: 05. 07. 2019, 17:10:56 »
A ještě jedna věc, prosím.

Přemýšlím, zda použít implicit flow nebo authorization_code. Ten produkt by měl být pro jednu společnost, která bude sdílet dokumenty, události, bude požadovat přečtení dokumentu atd ve zkratce... čili bych předpokládal, že by mělo být možné uchovat "secret" někde bezpečně (jinak než například v cookies nebo localStorage). Myslíte, že mám v mém případě použít ten authorization flow nebo implicit flow?

Mockrát děkuji
« Poslední změna: 05. 07. 2019, 17:12:32 od Arthnon »

Re:Nápad na bakalářskou práci se Spring
« Odpověď #48 kdy: 06. 07. 2019, 11:08:25 »
Používáte podporu pro resource server, která je součástí Spring Boot, takže JWT nebudete ověřovat ručně. O ověření se postará Spring Boot a z vašeho pohledu to bude fungovat stejně, jako jakýkoli jiný způsob přihlášení. Tzn. deklarujete, které části aplikace může používat anonymní uživatel, které přihlášený, případně jakou roli musí mít uživatel, aby mohl použít danou službu. Je dobré to ověřit tak, že vyzkoušíte přístup s platným tokenem, s expirovaným tokenem, s tokenem s nevalidním podpisem a bez tokenu. K aplikaci byste se měl dostat jenom s platným tokenem, v ostatních případech musí server vrátit nějakou chybu.

Implicit flow se už dnes nepoužívá. Byl navržený pro aplikace v prohlížeči, ale i tam už se dnes doporučuje používat authorization flow, akorát se v takovém případě nepoužívá client secret (protože u aplikace běžící kompletně v prohlížeči není kam ho schovat).

Re:Nápad na bakalářskou práci se Spring
« Odpověď #49 kdy: 06. 07. 2019, 15:03:13 »
A jakým způsobem zavolám potom ten request, abych dostal ten token, když použiju authorization code bez secretu?
Když v postmanovi zkouším se secretem, že nastavím basic auth. a zadám client_id a secret a do body dám grant_type, username, password, tak to funguje bez problémů. Ale pokud na autorizačním serveru dám pryč ten secret akorát, v POSTMANovi nechám Basic Auth., ale nezadám secret, tak mi to vrátí 401.

Ještě jsem četl nějaké články, kam uložit access_token a refresh_token a dočetl jsem se, že localStorage i sessionStorage nejsou bezpečné. Kam na tom front endu je tedy dobré to bezpečně uložit dle Vás? Nebo pokud bych nějakými způsoby ošetřil XSS útok na localStorage (whitelist atd), myslíte, že by to bylo ok? :-)

Děkuji
« Poslední změna: 06. 07. 2019, 15:05:42 od Arthnon »


Re:Nápad na bakalářskou práci se Spring
« Odpověď #50 kdy: 06. 07. 2019, 20:03:51 »
Spring musí být nakonfigurován, aby povolil ten přístup bez client secret – dá se k tomu něco najít např. zde: Spring Security OAuth 2.0 - client secret always required for authorization code grant.

Podle mne je local storage nebo session storage bezpečné úložiště pro ty tokeny – dostanou se k němu jenom skripty z příslušné domény. Pokud jde o SPA aplikaci, můžete ten token uložit i přímo v aplikaci, ale pak dojde k odhlášení uživatele když obnoví stránku (F5).

Re:Nápad na bakalářskou práci se Spring
« Odpověď #51 kdy: 09. 07. 2019, 22:52:22 »
Jakým bezpečným způsobem posílat access token na server s requestem?

Pokud udělám: https://localhost:8443/docs?access_token=12345 (a používám https) je to v pohodě? Nebo jakým jiným způsobem tu GET metodu poslat na server?

Respektive. Když dám v prohlížeči Sít (firefox), tak tu request na ten server vidím a vidím i ten access_token v tom URL samozřejmě...

Mockrát děkuji
« Poslední změna: 09. 07. 2019, 22:57:26 od Arthnon »

Re:Nápad na bakalářskou práci se Spring
« Odpověď #52 kdy: 10. 07. 2019, 09:50:06 »
Token pro autorizaci požadavků na resource server se obvykle posílá v hlavičce Authorization – použije se typ autentizace  „Bearer“ a pak je uvedený token. Tj. HTTP hlavička pak vypadá takhle:

Kód: [Vybrat]
Authorization: Bearer <token>

Resource server s podporou JWT by měl umět vyzvednout si JWT token z této hlavičky.

Re:Nápad na bakalářskou práci se Spring
« Odpověď #53 kdy: 16. 07. 2019, 12:15:32 »
Zdravím,
dneska jsem se dostal do úzkých s many-to-many mapováním s "extra column". Podařilo se mi to namapovat tak, aby se mi vytrvořily tabulky v databázi OK. Nicméně problém mám s tím ,že nevím, jakým způsobem uložit ten objekt do databáze...

Document
Kód: [Vybrat]
@JsonIgnore
    @OneToMany(mappedBy = "document")
    private List<UsersDocuments> documentsForUsers;

User
Kód: [Vybrat]
@OneToMany(mappedBy = "user", orphanRemoval = true, cascade= CascadeType.ALL)
private List<UsersDocuments> usersDocuments;

PK
Kód: [Vybrat]
@Embeddable
public class UserDocumentsId implements Serializable {

    @Column(name="user_id")
    private long user;

    @Column(name="document_id")
    private long document;

Kód: [Vybrat]
@Entity
@Table(name="users_documents")
public class UsersDocuments {

    @EmbeddedId
    private UserDocumentsId id;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("user")
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId("document")
    @JoinColumn(name = "document_id")
    private Document document;

    @Column(name="approval")
    private boolean approval;

Ukládání - nefunkční.
document.getDocumentsForUsers().add(userDocuments); hází NULL exception.
Kód: [Vybrat]
Optional<User> user = userService.getUserByEmail(createdBy);
        Document document = new Document(title, desc);
        document.setUploadDatetime(new Date());
        document.setUser(user.get());

        List<User> users = userService.getUsersByRoles(roles);


        for(User userx : users){
            UsersDocuments userDocuments = new UsersDocuments();
            UserDocumentsId id = new UserDocumentsId();

            // Construct the Id with user and document
            id.setUser(userx.getId());
            id.setDocument(document.getId());

            userDocuments.setId(id);
            userDocuments.setApproval(true);

            document.getDocumentsForUsers().add(userDocuments);
        }

        documentService.saveDocument(document);

Mockrát děkuji

Re:Nápad na bakalářskou práci se Spring
« Odpověď #54 kdy: 16. 07. 2019, 20:20:54 »
Vytváříte tam objekt Document a pravděpodobně nikde nenastavujete fieldu documentsForUsers nějakou hodnotu – v deklaraci to nemáte a nejspíš to není ani v konstruktoru. document.getDocumentsForUsers() pak vrací null a pokus o volání metody na null skončí NullPointerException.

Jinak s ORM vám asi nepomůžu, já mám k ORM vrozený odpor (podle mne je to nástroj, který dělá víc škody než užitku) a zatím se mu úspěšně vyhýbám… (Ne že bych nedělal i na projektech, kde se používá ORM, ale nemusím u nich ORM řešit.)

Re:Nápad na bakalářskou práci se Spring
« Odpověď #55 kdy: 17. 07. 2019, 19:51:17 »
Ještě Vás poprosím další věc ohledně toho oauthu.

Dočetl jsem se, že se má uvádět:
Content-Type": "application/x-www-form-urlencoded
Je nutné tento content-type mít vždy tento, nebo jen při autentizaci/refresh_token atd?

Mám problém s tím, že mi nejdou posílat requesty na server, když ta data jsou v body. Spring mi to nebere:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
Za předpokladu, že použiji:
application/json
Tak je vše v pohodě...

Re:Nápad na bakalářskou práci se Spring
« Odpověď #56 kdy: 17. 07. 2019, 20:26:02 »
Kód: [Vybrat]
@PutMapping(value = "", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    @Secured({("ROLE_USER"),("ROLE_SUPERIOR"), ("ROLE_ADMIN")})
    public void updateApproval(OAuth2Authentication auth, @RequestParam MultiValueMap<String, String> body){
        String email = auth.getName();
        long userId = userService.getUserByEmail(email).get().getId();

        String approvalString  = body.getFirst("approval");
        int approval = Integer.parseInt(approvalString);

        String doc_ids = body.getFirst("doc_id");
        long doc_id = Long.parseLong(doc_ids);
        documentService.updateSharing(userId, doc_id, approval);
    }

takhle jsem vyřešil ten problém, aby mi to bralo tenhle content-type a v AJAXu jsem neposlal JSON data... každopádně má otázka trvá... a zda je tento kod, co sem ted dávám v pořádku řešení.

Děkuji

Re:Nápad na bakalářskou práci se Spring
« Odpověď #57 kdy: 05. 08. 2019, 15:42:06 »
Zdravím,

je nějaký doporučený způsob, jak se vypořádat s XSS na straně springu? To znamená provéct XSSstrip všech requestů? Jak pro GET tak POST? Podařilo se mi udělat pouze pro GET, ale já bych potřeboval, aby to šlo pro všechno... jak pro GET, POST, PUT...

Děkuji :)

Re:Nápad na bakalářskou práci se Spring
« Odpověď #58 kdy: 12. 08. 2019, 10:32:37 »
Dočetl jsem se, že se má uvádět:
Content-Type": "application/x-www-form-urlencoded
Je nutné tento content-type mít vždy tento, nebo jen při autentizaci/refresh_token atd?

Mám problém s tím, že mi nejdou posílat requesty na server, když ta data jsou v body. Spring mi to nebere:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
Za předpokladu, že použiji:
application/json
Tak je vše v pohodě...
Content-Type určuje, v jakém formátu odesíláte data na server (a opačně v hlavičkách odpovědi říká, v jakém formátu je odpověď). Pro data posílaná v těle požadavku při OAuth je správný typ application/x-www-form-urlencoded – data se posílají, jako by to byl vyplněný HTML formulář. Spring by to v rámci autentizace měl zpracovat sám, pokud byste autentizaci implementoval sám, musí kontroler nakonfigurovat, aby tento typ dat přijímal (standardně Spring očekává data ve formátu JSON).

je nějaký doporučený způsob, jak se vypořádat s XSS na straně springu?
XSS byste měl řešit na straně view. Pokud uživatel na server odešle např. znak <, chce, aby se ten znak zobrazil – vy byste ho tedy měl normálně uložit do databáze. Při renderování obsahu ve view pak musíte zajistit, aby se speciální znaky escapovaly – např. < se musí vyrenderovat jako &lt; do zdrojového kódu HTML, protože ten znak má jinak v HTML speciální význam. Ale kdybyste chtěl ten samý text vyrenderovat třeba do PDF, nebudete řešit znak <, protože v PDF žádný speciální význam nemá.

Řešit to na straně vstupu je špatně, protože tím měníte data, která poslal uživatel – zobrazíte pak něco jiného, než poslal. Navíc pak očekáváte, že v databázi už máte všechna data vyčištěná a zobrazujete je bez escapování, což je dost „odvážné“. Navíc escapuje se podle použitého výstupního formátu, tj. jinak se escapuje pro HTML, jinak pro PDF, jinak pro čistý text – a třeba se jinak budou escapovat budoucí verze HTML. V databázi byste tedy měl mít uložen původní vstup od uživatele, protože nikdy nevíte, jak ho budete potřebovat escapovat v budoucnosti.

Re:Nápad na bakalářskou práci se Spring
« Odpověď #59 kdy: 13. 08. 2019, 17:38:39 »

je nějaký doporučený způsob, jak se vypořádat s XSS na straně springu?
XSS byste měl řešit na straně view. Pokud uživatel na server odešle např. znak <, chce, aby se ten znak zobrazil – vy byste ho tedy měl normálně uložit do databáze. Při renderování obsahu ve view pak musíte zajistit, aby se speciální znaky escapovaly – např. < se musí vyrenderovat jako &lt; do zdrojového kódu HTML, protože ten znak má jinak v HTML speciální význam. Ale kdybyste chtěl ten samý text vyrenderovat třeba do PDF, nebudete řešit znak <, protože v PDF žádný speciální význam nemá.

Řešit to na straně vstupu je špatně, protože tím měníte data, která poslal uživatel – zobrazíte pak něco jiného, než poslal. Navíc pak očekáváte, že v databázi už máte všechna data vyčištěná a zobrazujete je bez escapování, což je dost „odvážné“. Navíc escapuje se podle použitého výstupního formátu, tj. jinak se escapuje pro HTML, jinak pro PDF, jinak pro čistý text – a třeba se jinak budou escapovat budoucí verze HTML. V databázi byste tedy měl mít uložen původní vstup od uživatele, protože nikdy nevíte, jak ho budete potřebovat escapovat v budoucnosti.
[/quote]

Čili data, která přijdou na server bych neměl na vstupu nijak kontrolovat/odescapovávat a normálně je rovnou uložit do databáze? Čili na serveru nemusím dělat žádnou kontrolu vůči XSS útokům? Nebo to mám zkontrolovat (udělat escapování) těsně před tím, než pošlu data na view? Nebo poslat ta data přímo z DBa udělat escapování a kontrolu těch dat až na view při renderování dat?